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_nodemeta.h"
32 #include "content_mapnode.h"
35 SQLite format specification:
36 - Initially only replaces sectors/ and sectors2/
38 If map.sqlite does not exist in the save dir
39 or the block was not found in the database
40 the map will try to load from sectors folder.
41 In either case, map.sqlite will be created
42 and all future saves will save there.
44 Structure of map.sqlite:
55 Map::Map(std::ostream &dout):
59 /*m_sector_mutex.Init();
60 assert(m_sector_mutex.IsInitialized());*/
68 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
69 for(; i.atEnd() == false; i++)
71 MapSector *sector = i.getNode()->getValue();
76 void Map::addEventReceiver(MapEventReceiver *event_receiver)
78 m_event_receivers.insert(event_receiver, false);
81 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
83 if(m_event_receivers.find(event_receiver) == NULL)
85 m_event_receivers.remove(event_receiver);
88 void Map::dispatchEvent(MapEditEvent *event)
90 for(core::map<MapEventReceiver*, bool>::Iterator
91 i = m_event_receivers.getIterator();
92 i.atEnd()==false; i++)
94 MapEventReceiver* event_receiver = i.getNode()->getKey();
95 event_receiver->onMapEditEvent(event);
99 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
101 if(m_sector_cache != NULL && p == m_sector_cache_p){
102 MapSector * sector = m_sector_cache;
106 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
111 MapSector *sector = n->getValue();
113 // Cache the last result
114 m_sector_cache_p = p;
115 m_sector_cache = sector;
120 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
122 return getSectorNoGenerateNoExNoLock(p);
125 MapSector * Map::getSectorNoGenerate(v2s16 p)
127 MapSector *sector = getSectorNoGenerateNoEx(p);
129 throw InvalidPositionException();
134 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
136 v2s16 p2d(p3d.X, p3d.Z);
137 MapSector * sector = getSectorNoGenerateNoEx(p2d);
140 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
144 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
146 MapBlock *block = getBlockNoCreateNoEx(p3d);
148 throw InvalidPositionException();
152 bool Map::isNodeUnderground(v3s16 p)
154 v3s16 blockpos = getNodeBlockPos(p);
156 MapBlock * block = getBlockNoCreate(blockpos);
157 return block->getIsUnderground();
159 catch(InvalidPositionException &e)
165 bool Map::isValidPosition(v3s16 p)
167 v3s16 blockpos = getNodeBlockPos(p);
168 MapBlock *block = getBlockNoCreate(blockpos);
169 return (block != NULL);
172 // Returns a CONTENT_IGNORE node if not found
173 MapNode Map::getNodeNoEx(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock *block = getBlockNoCreateNoEx(blockpos);
178 return MapNode(CONTENT_IGNORE);
179 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
180 return block->getNodeNoCheck(relpos);
183 // throws InvalidPositionException if not found
184 MapNode Map::getNode(v3s16 p)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreateNoEx(blockpos);
189 throw InvalidPositionException();
190 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
191 return block->getNodeNoCheck(relpos);
194 // throws InvalidPositionException if not found
195 void Map::setNode(v3s16 p, MapNode & n)
197 v3s16 blockpos = getNodeBlockPos(p);
198 MapBlock *block = getBlockNoCreate(blockpos);
199 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
200 block->setNodeNoCheck(relpos, n);
205 Goes recursively through the neighbours of the node.
207 Alters only transparent nodes.
209 If the lighting of the neighbour is lower than the lighting of
210 the node was (before changing it to 0 at the step before), the
211 lighting of the neighbour is set to 0 and then the same stuff
212 repeats for the neighbour.
214 The ending nodes of the routine are stored in light_sources.
215 This is useful when a light is removed. In such case, this
216 routine can be called for the light node and then again for
217 light_sources to re-light the area without the removed light.
219 values of from_nodes are lighting values.
221 void Map::unspreadLight(enum LightBank bank,
222 core::map<v3s16, u8> & from_nodes,
223 core::map<v3s16, bool> & light_sources,
224 core::map<v3s16, MapBlock*> & modified_blocks)
227 v3s16(0,0,1), // back
229 v3s16(1,0,0), // right
230 v3s16(0,0,-1), // front
231 v3s16(0,-1,0), // bottom
232 v3s16(-1,0,0), // left
235 if(from_nodes.size() == 0)
238 u32 blockchangecount = 0;
240 core::map<v3s16, u8> unlighted_nodes;
241 core::map<v3s16, u8>::Iterator j;
242 j = from_nodes.getIterator();
245 Initialize block cache
248 MapBlock *block = NULL;
249 // Cache this a bit, too
250 bool block_checked_in_modified = false;
252 for(; j.atEnd() == false; j++)
254 v3s16 pos = j.getNode()->getKey();
255 v3s16 blockpos = getNodeBlockPos(pos);
257 // Only fetch a new block if the block position has changed
259 if(block == NULL || blockpos != blockpos_last){
260 block = getBlockNoCreate(blockpos);
261 blockpos_last = blockpos;
263 block_checked_in_modified = false;
267 catch(InvalidPositionException &e)
275 // Calculate relative position in block
276 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
278 // Get node straight from the block
279 MapNode n = block->getNode(relpos);
281 u8 oldlight = j.getNode()->getValue();
283 // Loop through 6 neighbors
284 for(u16 i=0; i<6; i++)
286 // Get the position of the neighbor node
287 v3s16 n2pos = pos + dirs[i];
289 // Get the block where the node is located
290 v3s16 blockpos = getNodeBlockPos(n2pos);
294 // Only fetch a new block if the block position has changed
296 if(block == NULL || blockpos != blockpos_last){
297 block = getBlockNoCreate(blockpos);
298 blockpos_last = blockpos;
300 block_checked_in_modified = false;
304 catch(InvalidPositionException &e)
309 // Calculate relative position in block
310 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
311 // Get node straight from the block
312 MapNode n2 = block->getNode(relpos);
314 bool changed = false;
316 //TODO: Optimize output by optimizing light_sources?
319 If the neighbor is dimmer than what was specified
320 as oldlight (the light of the previous node)
322 if(n2.getLight(bank) < oldlight)
325 And the neighbor is transparent and it has some light
327 if(n2.light_propagates() && n2.getLight(bank) != 0)
330 Set light to 0 and add to queue
333 u8 current_light = n2.getLight(bank);
334 n2.setLight(bank, 0);
335 block->setNode(relpos, n2);
337 unlighted_nodes.insert(n2pos, current_light);
341 Remove from light_sources if it is there
342 NOTE: This doesn't happen nearly at all
344 /*if(light_sources.find(n2pos))
346 std::cout<<"Removed from light_sources"<<std::endl;
347 light_sources.remove(n2pos);
352 if(light_sources.find(n2pos) != NULL)
353 light_sources.remove(n2pos);*/
356 light_sources.insert(n2pos, true);
359 // Add to modified_blocks
360 if(changed == true && block_checked_in_modified == false)
362 // If the block is not found in modified_blocks, add.
363 if(modified_blocks.find(blockpos) == NULL)
365 modified_blocks.insert(blockpos, block);
367 block_checked_in_modified = true;
370 catch(InvalidPositionException &e)
377 /*dstream<<"unspreadLight(): Changed block "
378 <<blockchangecount<<" times"
379 <<" for "<<from_nodes.size()<<" nodes"
382 if(unlighted_nodes.size() > 0)
383 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
387 A single-node wrapper of the above
389 void Map::unLightNeighbors(enum LightBank bank,
390 v3s16 pos, u8 lightwas,
391 core::map<v3s16, bool> & light_sources,
392 core::map<v3s16, MapBlock*> & modified_blocks)
394 core::map<v3s16, u8> from_nodes;
395 from_nodes.insert(pos, lightwas);
397 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
401 Lights neighbors of from_nodes, collects all them and then
404 void Map::spreadLight(enum LightBank bank,
405 core::map<v3s16, bool> & from_nodes,
406 core::map<v3s16, MapBlock*> & modified_blocks)
408 const v3s16 dirs[6] = {
409 v3s16(0,0,1), // back
411 v3s16(1,0,0), // right
412 v3s16(0,0,-1), // front
413 v3s16(0,-1,0), // bottom
414 v3s16(-1,0,0), // left
417 if(from_nodes.size() == 0)
420 u32 blockchangecount = 0;
422 core::map<v3s16, bool> lighted_nodes;
423 core::map<v3s16, bool>::Iterator j;
424 j = from_nodes.getIterator();
427 Initialize block cache
430 MapBlock *block = NULL;
431 // Cache this a bit, too
432 bool block_checked_in_modified = false;
434 for(; j.atEnd() == false; j++)
435 //for(; j != from_nodes.end(); j++)
437 v3s16 pos = j.getNode()->getKey();
439 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
440 v3s16 blockpos = getNodeBlockPos(pos);
442 // Only fetch a new block if the block position has changed
444 if(block == NULL || blockpos != blockpos_last){
445 block = getBlockNoCreate(blockpos);
446 blockpos_last = blockpos;
448 block_checked_in_modified = false;
452 catch(InvalidPositionException &e)
460 // Calculate relative position in block
461 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
463 // Get node straight from the block
464 MapNode n = block->getNode(relpos);
466 u8 oldlight = n.getLight(bank);
467 u8 newlight = diminish_light(oldlight);
469 // Loop through 6 neighbors
470 for(u16 i=0; i<6; i++){
471 // Get the position of the neighbor node
472 v3s16 n2pos = pos + dirs[i];
474 // Get the block where the node is located
475 v3s16 blockpos = getNodeBlockPos(n2pos);
479 // Only fetch a new block if the block position has changed
481 if(block == NULL || blockpos != blockpos_last){
482 block = getBlockNoCreate(blockpos);
483 blockpos_last = blockpos;
485 block_checked_in_modified = false;
489 catch(InvalidPositionException &e)
494 // Calculate relative position in block
495 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
496 // Get node straight from the block
497 MapNode n2 = block->getNode(relpos);
499 bool changed = false;
501 If the neighbor is brighter than the current node,
502 add to list (it will light up this node on its turn)
504 if(n2.getLight(bank) > undiminish_light(oldlight))
506 lighted_nodes.insert(n2pos, true);
507 //lighted_nodes.push_back(n2pos);
511 If the neighbor is dimmer than how much light this node
512 would spread on it, add to list
514 if(n2.getLight(bank) < newlight)
516 if(n2.light_propagates())
518 n2.setLight(bank, newlight);
519 block->setNode(relpos, n2);
520 lighted_nodes.insert(n2pos, true);
521 //lighted_nodes.push_back(n2pos);
526 // Add to modified_blocks
527 if(changed == true && block_checked_in_modified == false)
529 // If the block is not found in modified_blocks, add.
530 if(modified_blocks.find(blockpos) == NULL)
532 modified_blocks.insert(blockpos, block);
534 block_checked_in_modified = true;
537 catch(InvalidPositionException &e)
544 /*dstream<<"spreadLight(): Changed block "
545 <<blockchangecount<<" times"
546 <<" for "<<from_nodes.size()<<" nodes"
549 if(lighted_nodes.size() > 0)
550 spreadLight(bank, lighted_nodes, modified_blocks);
554 A single-node source variation of the above.
556 void Map::lightNeighbors(enum LightBank bank,
558 core::map<v3s16, MapBlock*> & modified_blocks)
560 core::map<v3s16, bool> from_nodes;
561 from_nodes.insert(pos, true);
562 spreadLight(bank, from_nodes, modified_blocks);
565 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
568 v3s16(0,0,1), // back
570 v3s16(1,0,0), // right
571 v3s16(0,0,-1), // front
572 v3s16(0,-1,0), // bottom
573 v3s16(-1,0,0), // left
576 u8 brightest_light = 0;
577 v3s16 brightest_pos(0,0,0);
578 bool found_something = false;
580 // Loop through 6 neighbors
581 for(u16 i=0; i<6; i++){
582 // Get the position of the neighbor node
583 v3s16 n2pos = p + dirs[i];
588 catch(InvalidPositionException &e)
592 if(n2.getLight(bank) > brightest_light || found_something == false){
593 brightest_light = n2.getLight(bank);
594 brightest_pos = n2pos;
595 found_something = true;
599 if(found_something == false)
600 throw InvalidPositionException();
602 return brightest_pos;
606 Propagates sunlight down from a node.
607 Starting point gets sunlight.
609 Returns the lowest y value of where the sunlight went.
611 Mud is turned into grass in where the sunlight stops.
613 s16 Map::propagateSunlight(v3s16 start,
614 core::map<v3s16, MapBlock*> & modified_blocks)
619 v3s16 pos(start.X, y, start.Z);
621 v3s16 blockpos = getNodeBlockPos(pos);
624 block = getBlockNoCreate(blockpos);
626 catch(InvalidPositionException &e)
631 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
632 MapNode n = block->getNode(relpos);
634 if(n.sunlight_propagates())
636 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
637 block->setNode(relpos, n);
639 modified_blocks.insert(blockpos, block);
643 /*// Turn mud into grass
644 if(n.getContent() == CONTENT_MUD)
646 n.setContent(CONTENT_GRASS);
647 block->setNode(relpos, n);
648 modified_blocks.insert(blockpos, block);
651 // Sunlight goes no further
658 void Map::updateLighting(enum LightBank bank,
659 core::map<v3s16, MapBlock*> & a_blocks,
660 core::map<v3s16, MapBlock*> & modified_blocks)
662 /*m_dout<<DTIME<<"Map::updateLighting(): "
663 <<a_blocks.size()<<" blocks."<<std::endl;*/
665 //TimeTaker timer("updateLighting");
669 //u32 count_was = modified_blocks.size();
671 core::map<v3s16, MapBlock*> blocks_to_update;
673 core::map<v3s16, bool> light_sources;
675 core::map<v3s16, u8> unlight_from;
677 core::map<v3s16, MapBlock*>::Iterator i;
678 i = a_blocks.getIterator();
679 for(; i.atEnd() == false; i++)
681 MapBlock *block = i.getNode()->getValue();
685 // Don't bother with dummy blocks.
689 v3s16 pos = block->getPos();
690 modified_blocks.insert(pos, block);
692 blocks_to_update.insert(pos, block);
695 Clear all light from block
697 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
698 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
699 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
704 MapNode n = block->getNode(v3s16(x,y,z));
705 u8 oldlight = n.getLight(bank);
707 block->setNode(v3s16(x,y,z), n);
709 // Collect borders for unlighting
710 if(x==0 || x == MAP_BLOCKSIZE-1
711 || y==0 || y == MAP_BLOCKSIZE-1
712 || z==0 || z == MAP_BLOCKSIZE-1)
714 v3s16 p_map = p + v3s16(
717 MAP_BLOCKSIZE*pos.Z);
718 unlight_from.insert(p_map, oldlight);
721 catch(InvalidPositionException &e)
724 This would happen when dealing with a
728 dstream<<"updateLighting(): InvalidPositionException"
733 if(bank == LIGHTBANK_DAY)
735 bool bottom_valid = block->propagateSunlight(light_sources);
737 // If bottom is valid, we're done.
741 else if(bank == LIGHTBANK_NIGHT)
743 // For night lighting, sunlight is not propagated
748 // Invalid lighting bank
752 /*dstream<<"Bottom for sunlight-propagated block ("
753 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
756 // Bottom sunlight is not valid; get the block and loop to it
760 block = getBlockNoCreate(pos);
762 catch(InvalidPositionException &e)
771 Enable this to disable proper lighting for speeding up map
772 generation for testing or whatever
775 //if(g_settings.get(""))
777 core::map<v3s16, MapBlock*>::Iterator i;
778 i = blocks_to_update.getIterator();
779 for(; i.atEnd() == false; i++)
781 MapBlock *block = i.getNode()->getValue();
782 v3s16 p = block->getPos();
783 block->setLightingExpired(false);
791 TimeTaker timer("unspreadLight");
792 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
797 u32 diff = modified_blocks.size() - count_was;
798 count_was = modified_blocks.size();
799 dstream<<"unspreadLight modified "<<diff<<std::endl;
803 TimeTaker timer("spreadLight");
804 spreadLight(bank, light_sources, modified_blocks);
809 u32 diff = modified_blocks.size() - count_was;
810 count_was = modified_blocks.size();
811 dstream<<"spreadLight modified "<<diff<<std::endl;
816 //MapVoxelManipulator vmanip(this);
818 // Make a manual voxel manipulator and load all the blocks
819 // that touch the requested blocks
820 ManualMapVoxelManipulator vmanip(this);
821 core::map<v3s16, MapBlock*>::Iterator i;
822 i = blocks_to_update.getIterator();
823 for(; i.atEnd() == false; i++)
825 MapBlock *block = i.getNode()->getValue();
826 v3s16 p = block->getPos();
828 // Add all surrounding blocks
829 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
832 Add all surrounding blocks that have up-to-date lighting
833 NOTE: This doesn't quite do the job (not everything
834 appropriate is lighted)
836 /*for(s16 z=-1; z<=1; z++)
837 for(s16 y=-1; y<=1; y++)
838 for(s16 x=-1; x<=1; x++)
841 MapBlock *block = getBlockNoCreateNoEx(p);
846 if(block->getLightingExpired())
848 vmanip.initialEmerge(p, p);
851 // Lighting of block will be updated completely
852 block->setLightingExpired(false);
856 //TimeTaker timer("unSpreadLight");
857 vmanip.unspreadLight(bank, unlight_from, light_sources);
860 //TimeTaker timer("spreadLight");
861 vmanip.spreadLight(bank, light_sources);
864 //TimeTaker timer("blitBack");
865 vmanip.blitBack(modified_blocks);
867 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
871 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
874 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
875 core::map<v3s16, MapBlock*> & modified_blocks)
877 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
878 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
881 Update information about whether day and night light differ
883 for(core::map<v3s16, MapBlock*>::Iterator
884 i = modified_blocks.getIterator();
885 i.atEnd() == false; i++)
887 MapBlock *block = i.getNode()->getValue();
888 block->updateDayNightDiff();
894 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
895 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
898 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
899 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
902 From this node to nodes underneath:
903 If lighting is sunlight (1.0), unlight neighbours and
908 v3s16 toppos = p + v3s16(0,1,0);
909 v3s16 bottompos = p + v3s16(0,-1,0);
911 bool node_under_sunlight = true;
912 core::map<v3s16, bool> light_sources;
915 If there is a node at top and it doesn't have sunlight,
916 there has not been any sunlight going down.
918 Otherwise there probably is.
921 MapNode topnode = getNode(toppos);
923 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
924 node_under_sunlight = false;
926 catch(InvalidPositionException &e)
932 If the new node is solid and there is grass below, change it to mud
934 if(content_features(n).walkable == true)
937 MapNode bottomnode = getNode(bottompos);
939 if(bottomnode.getContent() == CONTENT_GRASS
940 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
942 bottomnode.setContent(CONTENT_MUD);
943 setNode(bottompos, bottomnode);
946 catch(InvalidPositionException &e)
954 If the new node is mud and it is under sunlight, change it
957 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
959 n.setContent(CONTENT_GRASS);
964 Remove all light that has come out of this node
967 enum LightBank banks[] =
972 for(s32 i=0; i<2; i++)
974 enum LightBank bank = banks[i];
976 u8 lightwas = getNode(p).getLight(bank);
978 // Add the block of the added node to modified_blocks
979 v3s16 blockpos = getNodeBlockPos(p);
980 MapBlock * block = getBlockNoCreate(blockpos);
981 assert(block != NULL);
982 modified_blocks.insert(blockpos, block);
984 assert(isValidPosition(p));
986 // Unlight neighbours of node.
987 // This means setting light of all consequent dimmer nodes
989 // This also collects the nodes at the border which will spread
990 // light again into this.
991 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
997 If node lets sunlight through and is under sunlight, it has
1000 if(node_under_sunlight && content_features(n).sunlight_propagates)
1002 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1006 Set the node on the map
1015 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1018 NodeMetadata *meta = meta_proto->clone();
1019 /* lockable chest, insert the owner's name */
1020 if (meta->typeId() == CONTENT_LOCKABLE_CHEST)
1022 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
1023 lcm->setOwner(player_name);
1025 setNodeMetadata(p, meta);
1027 else if (n.getContent() == CONTENT_LOCKABLE_CHEST)
1029 LockingChestNodeMetadata *lcm = new LockingChestNodeMetadata();
1030 lcm->setOwner(player_name);
1031 setNodeMetadata(p, (NodeMetadata*)lcm);
1035 If node is under sunlight and doesn't let sunlight through,
1036 take all sunlighted nodes under it and clear light from them
1037 and from where the light has been spread.
1038 TODO: This could be optimized by mass-unlighting instead
1041 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1045 //m_dout<<DTIME<<"y="<<y<<std::endl;
1046 v3s16 n2pos(p.X, y, p.Z);
1050 n2 = getNode(n2pos);
1052 catch(InvalidPositionException &e)
1057 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1059 unLightNeighbors(LIGHTBANK_DAY,
1060 n2pos, n2.getLight(LIGHTBANK_DAY),
1061 light_sources, modified_blocks);
1062 n2.setLight(LIGHTBANK_DAY, 0);
1070 for(s32 i=0; i<2; i++)
1072 enum LightBank bank = banks[i];
1075 Spread light from all nodes that might be capable of doing so
1077 spreadLight(bank, light_sources, modified_blocks);
1081 Update information about whether day and night light differ
1083 for(core::map<v3s16, MapBlock*>::Iterator
1084 i = modified_blocks.getIterator();
1085 i.atEnd() == false; i++)
1087 MapBlock *block = i.getNode()->getValue();
1088 block->updateDayNightDiff();
1092 Add neighboring liquid nodes and the node itself if it is
1093 liquid (=water node was added) to transform queue.
1096 v3s16(0,0,0), // self
1097 v3s16(0,0,1), // back
1098 v3s16(0,1,0), // top
1099 v3s16(1,0,0), // right
1100 v3s16(0,0,-1), // front
1101 v3s16(0,-1,0), // bottom
1102 v3s16(-1,0,0), // left
1104 for(u16 i=0; i<7; i++)
1109 v3s16 p2 = p + dirs[i];
1111 MapNode n2 = getNode(p2);
1112 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1114 m_transforming_liquid.push_back(p2);
1117 }catch(InvalidPositionException &e)
1125 void Map::removeNodeAndUpdate(v3s16 p,
1126 core::map<v3s16, MapBlock*> &modified_blocks)
1128 /*PrintInfo(m_dout);
1129 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1130 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1132 bool node_under_sunlight = true;
1134 v3s16 toppos = p + v3s16(0,1,0);
1136 // Node will be replaced with this
1137 content_t replace_material = CONTENT_AIR;
1140 If there is a node at top and it doesn't have sunlight,
1141 there will be no sunlight going down.
1144 MapNode topnode = getNode(toppos);
1146 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1147 node_under_sunlight = false;
1149 catch(InvalidPositionException &e)
1153 core::map<v3s16, bool> light_sources;
1155 enum LightBank banks[] =
1160 for(s32 i=0; i<2; i++)
1162 enum LightBank bank = banks[i];
1165 Unlight neighbors (in case the node is a light source)
1167 unLightNeighbors(bank, p,
1168 getNode(p).getLight(bank),
1169 light_sources, modified_blocks);
1173 Remove node metadata
1176 removeNodeMetadata(p);
1180 This also clears the lighting.
1184 n.setContent(replace_material);
1187 for(s32 i=0; i<2; i++)
1189 enum LightBank bank = banks[i];
1192 Recalculate lighting
1194 spreadLight(bank, light_sources, modified_blocks);
1197 // Add the block of the removed node to modified_blocks
1198 v3s16 blockpos = getNodeBlockPos(p);
1199 MapBlock * block = getBlockNoCreate(blockpos);
1200 assert(block != NULL);
1201 modified_blocks.insert(blockpos, block);
1204 If the removed node was under sunlight, propagate the
1205 sunlight down from it and then light all neighbors
1206 of the propagated blocks.
1208 if(node_under_sunlight)
1210 s16 ybottom = propagateSunlight(p, modified_blocks);
1211 /*m_dout<<DTIME<<"Node was under sunlight. "
1212 "Propagating sunlight";
1213 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1215 for(; y >= ybottom; y--)
1217 v3s16 p2(p.X, y, p.Z);
1218 /*m_dout<<DTIME<<"lighting neighbors of node ("
1219 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1221 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1226 // Set the lighting of this node to 0
1227 // TODO: Is this needed? Lighting is cleared up there already.
1229 MapNode n = getNode(p);
1230 n.setLight(LIGHTBANK_DAY, 0);
1233 catch(InvalidPositionException &e)
1239 for(s32 i=0; i<2; i++)
1241 enum LightBank bank = banks[i];
1243 // Get the brightest neighbour node and propagate light from it
1244 v3s16 n2p = getBrightestNeighbour(bank, p);
1246 MapNode n2 = getNode(n2p);
1247 lightNeighbors(bank, n2p, modified_blocks);
1249 catch(InvalidPositionException &e)
1255 Update information about whether day and night light differ
1257 for(core::map<v3s16, MapBlock*>::Iterator
1258 i = modified_blocks.getIterator();
1259 i.atEnd() == false; i++)
1261 MapBlock *block = i.getNode()->getValue();
1262 block->updateDayNightDiff();
1266 Add neighboring liquid nodes and this node to transform queue.
1267 (it's vital for the node itself to get updated last.)
1270 v3s16(0,0,1), // back
1271 v3s16(0,1,0), // top
1272 v3s16(1,0,0), // right
1273 v3s16(0,0,-1), // front
1274 v3s16(0,-1,0), // bottom
1275 v3s16(-1,0,0), // left
1276 v3s16(0,0,0), // self
1278 for(u16 i=0; i<7; i++)
1283 v3s16 p2 = p + dirs[i];
1285 MapNode n2 = getNode(p2);
1286 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1288 m_transforming_liquid.push_back(p2);
1291 }catch(InvalidPositionException &e)
1297 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1300 event.type = MEET_ADDNODE;
1304 bool succeeded = true;
1306 core::map<v3s16, MapBlock*> modified_blocks;
1307 std::string st = std::string("");
1308 addNodeAndUpdate(p, n, modified_blocks, st);
1310 // Copy modified_blocks to event
1311 for(core::map<v3s16, MapBlock*>::Iterator
1312 i = modified_blocks.getIterator();
1313 i.atEnd()==false; i++)
1315 event.modified_blocks.insert(i.getNode()->getKey(), false);
1318 catch(InvalidPositionException &e){
1322 dispatchEvent(&event);
1327 bool Map::removeNodeWithEvent(v3s16 p)
1330 event.type = MEET_REMOVENODE;
1333 bool succeeded = true;
1335 core::map<v3s16, MapBlock*> modified_blocks;
1336 removeNodeAndUpdate(p, modified_blocks);
1338 // Copy modified_blocks to event
1339 for(core::map<v3s16, MapBlock*>::Iterator
1340 i = modified_blocks.getIterator();
1341 i.atEnd()==false; i++)
1343 event.modified_blocks.insert(i.getNode()->getKey(), false);
1346 catch(InvalidPositionException &e){
1350 dispatchEvent(&event);
1355 bool Map::dayNightDiffed(v3s16 blockpos)
1358 v3s16 p = blockpos + v3s16(0,0,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->dayNightDiffed())
1363 catch(InvalidPositionException &e){}
1366 v3s16 p = blockpos + v3s16(-1,0,0);
1367 MapBlock *b = getBlockNoCreate(p);
1368 if(b->dayNightDiffed())
1371 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(0,-1,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->dayNightDiffed())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,0,-1);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->dayNightDiffed())
1385 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(1,0,0);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->dayNightDiffed())
1393 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(0,1,0);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->dayNightDiffed())
1400 catch(InvalidPositionException &e){}
1402 v3s16 p = blockpos + v3s16(0,0,1);
1403 MapBlock *b = getBlockNoCreate(p);
1404 if(b->dayNightDiffed())
1407 catch(InvalidPositionException &e){}
1413 Updates usage timers
1415 void Map::timerUpdate(float dtime, float unload_timeout,
1416 core::list<v3s16> *unloaded_blocks)
1418 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1420 core::list<v2s16> sector_deletion_queue;
1421 u32 deleted_blocks_count = 0;
1422 u32 saved_blocks_count = 0;
1424 core::map<v2s16, MapSector*>::Iterator si;
1427 si = m_sectors.getIterator();
1428 for(; si.atEnd() == false; si++)
1430 MapSector *sector = si.getNode()->getValue();
1432 bool all_blocks_deleted = true;
1434 core::list<MapBlock*> blocks;
1435 sector->getBlocks(blocks);
1437 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1438 i != blocks.end(); i++)
1440 MapBlock *block = (*i);
1442 block->incrementUsageTimer(dtime);
1444 if(block->getUsageTimer() > unload_timeout)
1446 v3s16 p = block->getPos();
1449 if(block->getModified() != MOD_STATE_CLEAN
1450 && save_before_unloading)
1453 saved_blocks_count++;
1456 // Delete from memory
1457 sector->deleteBlock(block);
1460 unloaded_blocks->push_back(p);
1462 deleted_blocks_count++;
1466 all_blocks_deleted = false;
1470 if(all_blocks_deleted)
1472 sector_deletion_queue.push_back(si.getNode()->getKey());
1477 // Finally delete the empty sectors
1478 deleteSectors(sector_deletion_queue);
1480 if(deleted_blocks_count != 0)
1482 PrintInfo(dstream); // ServerMap/ClientMap:
1483 dstream<<"Unloaded "<<deleted_blocks_count
1484 <<" blocks from memory";
1485 if(save_before_unloading)
1486 dstream<<", of which "<<saved_blocks_count<<" were written";
1487 dstream<<"."<<std::endl;
1491 void Map::deleteSectors(core::list<v2s16> &list)
1493 core::list<v2s16>::Iterator j;
1494 for(j=list.begin(); j!=list.end(); j++)
1496 MapSector *sector = m_sectors[*j];
1497 // If sector is in sector cache, remove it from there
1498 if(m_sector_cache == sector)
1499 m_sector_cache = NULL;
1500 // Remove from map and delete
1501 m_sectors.remove(*j);
1507 void Map::unloadUnusedData(float timeout,
1508 core::list<v3s16> *deleted_blocks)
1510 core::list<v2s16> sector_deletion_queue;
1511 u32 deleted_blocks_count = 0;
1512 u32 saved_blocks_count = 0;
1514 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1515 for(; si.atEnd() == false; si++)
1517 MapSector *sector = si.getNode()->getValue();
1519 bool all_blocks_deleted = true;
1521 core::list<MapBlock*> blocks;
1522 sector->getBlocks(blocks);
1523 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1524 i != blocks.end(); i++)
1526 MapBlock *block = (*i);
1528 if(block->getUsageTimer() > timeout)
1531 if(block->getModified() != MOD_STATE_CLEAN)
1534 saved_blocks_count++;
1536 // Delete from memory
1537 sector->deleteBlock(block);
1538 deleted_blocks_count++;
1542 all_blocks_deleted = false;
1546 if(all_blocks_deleted)
1548 sector_deletion_queue.push_back(si.getNode()->getKey());
1552 deleteSectors(sector_deletion_queue);
1554 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1555 <<", of which "<<saved_blocks_count<<" were wr."
1558 //return sector_deletion_queue.getSize();
1559 //return deleted_blocks_count;
1563 void Map::PrintInfo(std::ostream &out)
1568 #define WATER_DROP_BOOST 4
1572 NEIGHBOR_SAME_LEVEL,
1575 struct NodeNeighbor {
1581 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1583 DSTACK(__FUNCTION_NAME);
1584 //TimeTaker timer("transformLiquids()");
1587 u32 initial_size = m_transforming_liquid.size();
1589 /*if(initial_size != 0)
1590 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1592 // list of nodes that due to viscosity have not reached their max level height
1593 UniqueQueue<v3s16> must_reflow;
1595 // List of MapBlocks that will require a lighting update (due to lava)
1596 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1598 while(m_transforming_liquid.size() != 0)
1600 // This should be done here so that it is done when continue is used
1601 if(loopcount >= initial_size * 3)
1606 Get a queued transforming liquid node
1608 v3s16 p0 = m_transforming_liquid.pop_front();
1610 MapNode n0 = getNodeNoEx(p0);
1613 Collect information about current node
1615 s8 liquid_level = -1;
1616 u8 liquid_kind = CONTENT_IGNORE;
1617 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1618 switch (liquid_type) {
1620 liquid_level = LIQUID_LEVEL_SOURCE;
1621 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1623 case LIQUID_FLOWING:
1624 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1625 liquid_kind = n0.getContent();
1628 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1629 // continue with the next node.
1630 if (n0.getContent() != CONTENT_AIR)
1632 liquid_kind = CONTENT_AIR;
1637 Collect information about the environment
1639 const v3s16 *dirs = g_6dirs;
1640 NodeNeighbor sources[6]; // surrounding sources
1641 int num_sources = 0;
1642 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1644 NodeNeighbor airs[6]; // surrounding air
1646 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1647 int num_neutrals = 0;
1648 bool flowing_down = false;
1649 for (u16 i = 0; i < 6; i++) {
1650 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1653 nt = NEIGHBOR_UPPER;
1656 nt = NEIGHBOR_LOWER;
1659 v3s16 npos = p0 + dirs[i];
1660 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1661 switch (content_features(nb.n.getContent()).liquid_type) {
1663 if (nb.n.getContent() == CONTENT_AIR) {
1664 airs[num_airs++] = nb;
1665 // if the current node is a water source the neighbor
1666 // should be enqueded for transformation regardless of whether the
1667 // current node changes or not.
1668 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1669 m_transforming_liquid.push_back(npos);
1670 // if the current node happens to be a flowing node, it will start to flow down here.
1671 if (nb.t == NEIGHBOR_LOWER) {
1672 flowing_down = true;
1675 neutrals[num_neutrals++] = nb;
1679 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1680 if (liquid_kind == CONTENT_AIR)
1681 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1682 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1683 neutrals[num_neutrals++] = nb;
1685 sources[num_sources++] = nb;
1688 case LIQUID_FLOWING:
1689 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1690 if (liquid_kind == CONTENT_AIR)
1691 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1692 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1693 neutrals[num_neutrals++] = nb;
1695 flows[num_flows++] = nb;
1696 if (nb.t == NEIGHBOR_LOWER)
1697 flowing_down = true;
1704 decide on the type (and possibly level) of the current node
1706 content_t new_node_content;
1707 s8 new_node_level = -1;
1708 s8 max_node_level = -1;
1709 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1710 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1711 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1712 // it's perfectly safe to use liquid_kind here to determine the new node content.
1713 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1714 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1715 // liquid_kind is set properly, see above
1716 new_node_content = liquid_kind;
1717 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1719 // no surrounding sources, so get the maximum level that can flow into this node
1720 for (u16 i = 0; i < num_flows; i++) {
1721 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1722 switch (flows[i].t) {
1723 case NEIGHBOR_UPPER:
1724 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1725 max_node_level = LIQUID_LEVEL_MAX;
1726 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1727 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1728 } else if (nb_liquid_level > max_node_level)
1729 max_node_level = nb_liquid_level;
1731 case NEIGHBOR_LOWER:
1733 case NEIGHBOR_SAME_LEVEL:
1734 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1735 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1736 max_node_level = nb_liquid_level - 1;
1742 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1743 if (viscosity > 1 && max_node_level != liquid_level) {
1744 // amount to gain, limited by viscosity
1745 // must be at least 1 in absolute value
1746 s8 level_inc = max_node_level - liquid_level;
1747 if (level_inc < -viscosity || level_inc > viscosity)
1748 new_node_level = liquid_level + level_inc/viscosity;
1749 else if (level_inc < 0)
1750 new_node_level = liquid_level - 1;
1751 else if (level_inc > 0)
1752 new_node_level = liquid_level + 1;
1753 if (new_node_level != max_node_level)
1754 must_reflow.push_back(p0);
1756 new_node_level = max_node_level;
1758 if (new_node_level >= 0)
1759 new_node_content = liquid_kind;
1761 new_node_content = CONTENT_AIR;
1766 check if anything has changed. if not, just continue with the next node.
1768 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1769 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1770 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1776 update the current node
1778 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1779 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1780 // set level to last 3 bits, flowing down bit to 4th bit
1781 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1783 // set the liquid level and flow bit to 0
1784 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1786 n0.setContent(new_node_content);
1788 v3s16 blockpos = getNodeBlockPos(p0);
1789 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1791 modified_blocks.insert(blockpos, block);
1792 // If node emits light, MapBlock requires lighting update
1793 if(content_features(n0).light_source != 0)
1794 lighting_modified_blocks[block->getPos()] = block;
1798 enqueue neighbors for update if neccessary
1800 switch (content_features(n0.getContent()).liquid_type) {
1802 case LIQUID_FLOWING:
1803 // make sure source flows into all neighboring nodes
1804 for (u16 i = 0; i < num_flows; i++)
1805 if (flows[i].t != NEIGHBOR_UPPER)
1806 m_transforming_liquid.push_back(flows[i].p);
1807 for (u16 i = 0; i < num_airs; i++)
1808 if (airs[i].t != NEIGHBOR_UPPER)
1809 m_transforming_liquid.push_back(airs[i].p);
1812 // this flow has turned to air; neighboring flows might need to do the same
1813 for (u16 i = 0; i < num_flows; i++)
1814 m_transforming_liquid.push_back(flows[i].p);
1818 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1819 while (must_reflow.size() > 0)
1820 m_transforming_liquid.push_back(must_reflow.pop_front());
1821 updateLighting(lighting_modified_blocks, modified_blocks);
1824 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1826 v3s16 blockpos = getNodeBlockPos(p);
1827 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1828 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1831 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1835 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1839 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1841 v3s16 blockpos = getNodeBlockPos(p);
1842 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1843 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1846 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1850 block->m_node_metadata.set(p_rel, meta);
1853 void Map::removeNodeMetadata(v3s16 p)
1855 v3s16 blockpos = getNodeBlockPos(p);
1856 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1857 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1860 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1864 block->m_node_metadata.remove(p_rel);
1867 void Map::nodeMetadataStep(float dtime,
1868 core::map<v3s16, MapBlock*> &changed_blocks)
1872 Currently there is no way to ensure that all the necessary
1873 blocks are loaded when this is run. (They might get unloaded)
1874 NOTE: ^- Actually, that might not be so. In a quick test it
1875 reloaded a block with a furnace when I walked back to it from
1878 core::map<v2s16, MapSector*>::Iterator si;
1879 si = m_sectors.getIterator();
1880 for(; si.atEnd() == false; si++)
1882 MapSector *sector = si.getNode()->getValue();
1883 core::list< MapBlock * > sectorblocks;
1884 sector->getBlocks(sectorblocks);
1885 core::list< MapBlock * >::Iterator i;
1886 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1888 MapBlock *block = *i;
1889 bool changed = block->m_node_metadata.step(dtime);
1891 changed_blocks[block->getPos()] = block;
1900 ServerMap::ServerMap(std::string savedir):
1903 m_map_metadata_changed(true),
1905 m_database_read(NULL),
1906 m_database_write(NULL)
1908 dstream<<__FUNCTION_NAME<<std::endl;
1910 //m_chunksize = 8; // Takes a few seconds
1912 if (g_settings.get("fixed_map_seed").empty())
1914 m_seed = (((u64)(myrand()%0xffff)<<0)
1915 + ((u64)(myrand()%0xffff)<<16)
1916 + ((u64)(myrand()%0xffff)<<32)
1917 + ((u64)(myrand()%0xffff)<<48));
1921 m_seed = g_settings.getU64("fixed_map_seed");
1925 Experimental and debug stuff
1932 Try to load map; if not found, create a new one.
1935 m_savedir = savedir;
1936 m_map_saving_enabled = false;
1940 // If directory exists, check contents and load if possible
1941 if(fs::PathExists(m_savedir))
1943 // If directory is empty, it is safe to save into it.
1944 if(fs::GetDirListing(m_savedir).size() == 0)
1946 dstream<<DTIME<<"Server: Empty save directory is valid."
1948 m_map_saving_enabled = true;
1953 // Load map metadata (seed, chunksize)
1956 catch(FileNotGoodException &e){
1957 dstream<<DTIME<<"WARNING: Could not load map metadata"
1958 //<<" Disabling chunk-based generator."
1964 // Load chunk metadata
1967 catch(FileNotGoodException &e){
1968 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1969 <<" Disabling chunk-based generator."
1974 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1975 "metadata and sector (0,0) from "<<savedir<<
1976 ", assuming valid save directory."
1979 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1980 <<"and chunk metadata from "<<savedir
1981 <<", assuming valid save directory."
1984 m_map_saving_enabled = true;
1985 // Map loaded, not creating new one
1989 // If directory doesn't exist, it is safe to save to it
1991 m_map_saving_enabled = true;
1994 catch(std::exception &e)
1996 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1997 <<", exception: "<<e.what()<<std::endl;
1998 dstream<<"Please remove the map or fix it."<<std::endl;
1999 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
2002 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
2004 // Create zero sector
2005 emergeSector(v2s16(0,0));
2007 // Initially write whole map
2011 ServerMap::~ServerMap()
2013 dstream<<__FUNCTION_NAME<<std::endl;
2017 if(m_map_saving_enabled)
2019 // Save only changed parts
2021 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2025 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2028 catch(std::exception &e)
2030 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2031 <<", exception: "<<e.what()<<std::endl;
2035 Close database if it was opened
2038 sqlite3_finalize(m_database_read);
2039 if(m_database_write)
2040 sqlite3_finalize(m_database_write);
2042 sqlite3_close(m_database);
2048 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2049 for(; i.atEnd() == false; i++)
2051 MapChunk *chunk = i.getNode()->getValue();
2057 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2059 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2060 if(enable_mapgen_debug_info)
2061 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2062 <<blockpos.Z<<")"<<std::endl;
2064 // Do nothing if not inside limits (+-1 because of neighbors)
2065 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2066 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2072 data->no_op = false;
2073 data->seed = m_seed;
2074 data->blockpos = blockpos;
2077 Create the whole area of this and the neighboring blocks
2080 //TimeTaker timer("initBlockMake() create area");
2082 for(s16 x=-1; x<=1; x++)
2083 for(s16 z=-1; z<=1; z++)
2085 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2086 // Sector metadata is loaded from disk if not already loaded.
2087 ServerMapSector *sector = createSector(sectorpos);
2090 for(s16 y=-1; y<=1; y++)
2092 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2093 //MapBlock *block = createBlock(p);
2094 // 1) get from memory, 2) load from disk
2095 MapBlock *block = emergeBlock(p, false);
2096 // 3) create a blank one
2099 block = createBlock(p);
2102 Block gets sunlight if this is true.
2104 Refer to the map generator heuristics.
2106 bool ug = mapgen::block_is_underground(data->seed, p);
2107 block->setIsUnderground(ug);
2110 // Lighting will not be valid after make_chunk is called
2111 block->setLightingExpired(true);
2112 // Lighting will be calculated
2113 //block->setLightingExpired(false);
2119 Now we have a big empty area.
2121 Make a ManualMapVoxelManipulator that contains this and the
2125 // The area that contains this block and it's neighbors
2126 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2127 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2129 data->vmanip = new ManualMapVoxelManipulator(this);
2130 //data->vmanip->setMap(this);
2134 //TimeTaker timer("initBlockMake() initialEmerge");
2135 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2138 // Data is ready now.
2141 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2142 core::map<v3s16, MapBlock*> &changed_blocks)
2144 v3s16 blockpos = data->blockpos;
2145 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2146 <<blockpos.Z<<")"<<std::endl;*/
2150 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2154 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2156 /*dstream<<"Resulting vmanip:"<<std::endl;
2157 data->vmanip.print(dstream);*/
2160 Blit generated stuff to map
2161 NOTE: blitBackAll adds nearly everything to changed_blocks
2165 //TimeTaker timer("finishBlockMake() blitBackAll");
2166 data->vmanip->blitBackAll(&changed_blocks);
2169 if(enable_mapgen_debug_info)
2170 dstream<<"finishBlockMake: changed_blocks.size()="
2171 <<changed_blocks.size()<<std::endl;
2174 Copy transforming liquid information
2176 while(data->transforming_liquid.size() > 0)
2178 v3s16 p = data->transforming_liquid.pop_front();
2179 m_transforming_liquid.push_back(p);
2185 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2189 Set is_underground flag for lighting with sunlight.
2191 Refer to map generator heuristics.
2193 NOTE: This is done in initChunkMake
2195 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2199 Add sunlight to central block.
2200 This makes in-dark-spawning monsters to not flood the whole thing.
2201 Do not spread the light, though.
2203 /*core::map<v3s16, bool> light_sources;
2204 bool black_air_left = false;
2205 block->propagateSunlight(light_sources, true, &black_air_left);*/
2208 NOTE: Lighting and object adding shouldn't really be here, but
2209 lighting is a bit tricky to move properly to makeBlock.
2210 TODO: Do this the right way anyway, that is, move it to makeBlock.
2211 - There needs to be some way for makeBlock to report back if
2212 the lighting update is going further down because of the
2213 new block blocking light
2218 NOTE: This takes ~60ms, TODO: Investigate why
2221 TimeTaker t("finishBlockMake lighting update");
2223 core::map<v3s16, MapBlock*> lighting_update_blocks;
2226 lighting_update_blocks.insert(block->getPos(), block);
2231 v3s16 p = block->getPos()+v3s16(x,1,z);
2232 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2236 // All modified blocks
2237 // NOTE: Should this be done? If this is not done, then the lighting
2238 // of the others will be updated in a different place, one by one, i
2239 // think... or they might not? Well, at least they are left marked as
2240 // "lighting expired"; it seems that is not handled at all anywhere,
2241 // so enabling this will slow it down A LOT because otherwise it
2242 // would not do this at all. This causes the black trees.
2243 for(core::map<v3s16, MapBlock*>::Iterator
2244 i = changed_blocks.getIterator();
2245 i.atEnd() == false; i++)
2247 lighting_update_blocks.insert(i.getNode()->getKey(),
2248 i.getNode()->getValue());
2250 /*// Also force-add all the upmost blocks for proper sunlight
2251 for(s16 x=-1; x<=1; x++)
2252 for(s16 z=-1; z<=1; z++)
2254 v3s16 p = block->getPos()+v3s16(x,1,z);
2255 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2258 updateLighting(lighting_update_blocks, changed_blocks);
2261 Set lighting to non-expired state in all of them.
2262 This is cheating, but it is not fast enough if all of them
2263 would actually be updated.
2265 for(s16 x=-1; x<=1; x++)
2266 for(s16 y=-1; y<=1; y++)
2267 for(s16 z=-1; z<=1; z++)
2269 v3s16 p = block->getPos()+v3s16(x,y,z);
2270 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2273 if(enable_mapgen_debug_info == false)
2274 t.stop(true); // Hide output
2278 Add random objects to block
2280 mapgen::add_random_objects(block);
2283 Go through changed blocks
2285 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2286 i.atEnd() == false; i++)
2288 MapBlock *block = i.getNode()->getValue();
2291 Update day/night difference cache of the MapBlocks
2293 block->updateDayNightDiff();
2295 Set block as modified
2297 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2301 Set central block as generated
2303 block->setGenerated(true);
2306 Save changed parts of map
2307 NOTE: Will be saved later.
2311 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2312 <<blockpos.Z<<")"<<std::endl;*/
2314 if(enable_mapgen_debug_info)
2317 Analyze resulting blocks
2319 for(s16 x=-1; x<=1; x++)
2320 for(s16 y=-1; y<=1; y++)
2321 for(s16 z=-1; z<=1; z++)
2323 v3s16 p = block->getPos()+v3s16(x,y,z);
2324 MapBlock *block = getBlockNoCreateNoEx(p);
2326 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2327 dstream<<"Generated "<<spos<<": "
2328 <<analyze_block(block)<<std::endl;
2336 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2338 DSTACKF("%s: p2d=(%d,%d)",
2343 Check if it exists already in memory
2345 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2350 Try to load it from disk (with blocks)
2352 //if(loadSectorFull(p2d) == true)
2355 Try to load metadata from disk
2358 if(loadSectorMeta(p2d) == true)
2360 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2363 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2364 throw InvalidPositionException("");
2370 Do not create over-limit
2372 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2373 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2374 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2375 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2376 throw InvalidPositionException("createSector(): pos. over limit");
2379 Generate blank sector
2382 sector = new ServerMapSector(this, p2d);
2384 // Sector position on map in nodes
2385 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2390 m_sectors.insert(p2d, sector);
2396 This is a quick-hand function for calling makeBlock().
2398 MapBlock * ServerMap::generateBlock(
2400 core::map<v3s16, MapBlock*> &modified_blocks
2403 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2405 /*dstream<<"generateBlock(): "
2406 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2409 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2411 TimeTaker timer("generateBlock");
2413 //MapBlock *block = original_dummy;
2415 v2s16 p2d(p.X, p.Z);
2416 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2419 Do not generate over-limit
2421 if(blockpos_over_limit(p))
2423 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2424 throw InvalidPositionException("generateBlock(): pos. over limit");
2428 Create block make data
2430 mapgen::BlockMakeData data;
2431 initBlockMake(&data, p);
2437 TimeTaker t("mapgen::make_block()");
2438 mapgen::make_block(&data);
2440 if(enable_mapgen_debug_info == false)
2441 t.stop(true); // Hide output
2445 Blit data back on map, update lighting, add mobs and whatever this does
2447 finishBlockMake(&data, modified_blocks);
2452 MapBlock *block = getBlockNoCreateNoEx(p);
2460 bool erroneus_content = false;
2461 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2462 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2463 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2466 MapNode n = block->getNode(p);
2467 if(n.getContent() == CONTENT_IGNORE)
2469 dstream<<"CONTENT_IGNORE at "
2470 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2472 erroneus_content = true;
2476 if(erroneus_content)
2485 Generate a completely empty block
2489 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2490 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2492 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2496 n.setContent(CONTENT_AIR);
2498 n.setContent(CONTENT_STONE);
2499 block->setNode(v3s16(x0,y0,z0), n);
2505 if(enable_mapgen_debug_info == false)
2506 timer.stop(true); // Hide output
2511 MapBlock * ServerMap::createBlock(v3s16 p)
2513 DSTACKF("%s: p=(%d,%d,%d)",
2514 __FUNCTION_NAME, p.X, p.Y, p.Z);
2517 Do not create over-limit
2519 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2520 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2521 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2522 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2523 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2524 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2525 throw InvalidPositionException("createBlock(): pos. over limit");
2527 v2s16 p2d(p.X, p.Z);
2530 This will create or load a sector if not found in memory.
2531 If block exists on disk, it will be loaded.
2533 NOTE: On old save formats, this will be slow, as it generates
2534 lighting on blocks for them.
2536 ServerMapSector *sector;
2538 sector = (ServerMapSector*)createSector(p2d);
2539 assert(sector->getId() == MAPSECTOR_SERVER);
2541 catch(InvalidPositionException &e)
2543 dstream<<"createBlock: createSector() failed"<<std::endl;
2547 NOTE: This should not be done, or at least the exception
2548 should not be passed on as std::exception, because it
2549 won't be catched at all.
2551 /*catch(std::exception &e)
2553 dstream<<"createBlock: createSector() failed: "
2554 <<e.what()<<std::endl;
2559 Try to get a block from the sector
2562 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2565 if(block->isDummy())
2570 block = sector->createBlankBlock(block_y);
2574 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2576 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2578 p.X, p.Y, p.Z, allow_generate);
2581 MapBlock *block = getBlockNoCreateNoEx(p);
2582 if(block && block->isDummy() == false)
2587 MapBlock *block = loadBlock(p);
2594 core::map<v3s16, MapBlock*> modified_blocks;
2595 MapBlock *block = generateBlock(p, modified_blocks);
2599 event.type = MEET_OTHER;
2602 // Copy modified_blocks to event
2603 for(core::map<v3s16, MapBlock*>::Iterator
2604 i = modified_blocks.getIterator();
2605 i.atEnd()==false; i++)
2607 event.modified_blocks.insert(i.getNode()->getKey(), false);
2611 dispatchEvent(&event);
2622 Do not generate over-limit
2624 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2625 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2626 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2627 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2628 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2629 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2630 throw InvalidPositionException("emergeBlock(): pos. over limit");
2632 v2s16 p2d(p.X, p.Z);
2635 This will create or load a sector if not found in memory.
2636 If block exists on disk, it will be loaded.
2638 ServerMapSector *sector;
2640 sector = createSector(p2d);
2641 //sector = emergeSector(p2d, changed_blocks);
2643 catch(InvalidPositionException &e)
2645 dstream<<"emergeBlock: createSector() failed: "
2646 <<e.what()<<std::endl;
2647 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2649 <<"You could try to delete it."<<std::endl;
2652 catch(VersionMismatchException &e)
2654 dstream<<"emergeBlock: createSector() failed: "
2655 <<e.what()<<std::endl;
2656 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2658 <<"You could try to delete it."<<std::endl;
2663 Try to get a block from the sector
2666 bool does_not_exist = false;
2667 bool lighting_expired = false;
2668 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2670 // If not found, try loading from disk
2673 block = loadBlock(p);
2679 does_not_exist = true;
2681 else if(block->isDummy() == true)
2683 does_not_exist = true;
2685 else if(block->getLightingExpired())
2687 lighting_expired = true;
2692 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2697 If block was not found on disk and not going to generate a
2698 new one, make sure there is a dummy block in place.
2700 if(only_from_disk && (does_not_exist || lighting_expired))
2702 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2706 // Create dummy block
2707 block = new MapBlock(this, p, true);
2709 // Add block to sector
2710 sector->insertBlock(block);
2716 //dstream<<"Not found on disk, generating."<<std::endl;
2718 //TimeTaker("emergeBlock() generate");
2720 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2723 If the block doesn't exist, generate the block.
2727 block = generateBlock(p, block, sector, changed_blocks,
2728 lighting_invalidated_blocks);
2731 if(lighting_expired)
2733 lighting_invalidated_blocks.insert(p, block);
2738 Initially update sunlight
2741 core::map<v3s16, bool> light_sources;
2742 bool black_air_left = false;
2743 bool bottom_invalid =
2744 block->propagateSunlight(light_sources, true,
2747 // If sunlight didn't reach everywhere and part of block is
2748 // above ground, lighting has to be properly updated
2749 //if(black_air_left && some_part_underground)
2752 lighting_invalidated_blocks[block->getPos()] = block;
2757 lighting_invalidated_blocks[block->getPos()] = block;
2766 s16 ServerMap::findGroundLevel(v2s16 p2d)
2770 Uh, just do something random...
2772 // Find existing map from top to down
2775 v3s16 p(p2d.X, max, p2d.Y);
2776 for(; p.Y>min; p.Y--)
2778 MapNode n = getNodeNoEx(p);
2779 if(n.getContent() != CONTENT_IGNORE)
2784 // If this node is not air, go to plan b
2785 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2787 // Search existing walkable and return it
2788 for(; p.Y>min; p.Y--)
2790 MapNode n = getNodeNoEx(p);
2791 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2800 Determine from map generator noise functions
2803 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2806 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2807 //return (s16)level;
2810 void ServerMap::createDatabase() {
2813 e = sqlite3_exec(m_database,
2814 "CREATE TABLE IF NOT EXISTS `blocks` ("
2815 "`pos` INT NOT NULL PRIMARY KEY,"
2818 , NULL, NULL, NULL);
2819 if(e == SQLITE_ABORT)
2820 throw FileNotGoodException("Could not create database structure");
2822 dstream<<"Server: Database structure was created";
2825 void ServerMap::verifyDatabase() {
2830 std::string dbp = m_savedir + "/map.sqlite";
2831 bool needs_create = false;
2835 Open the database connection
2838 createDirs(m_savedir);
2840 if(!fs::PathExists(dbp))
2841 needs_create = true;
2843 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2844 if(d != SQLITE_OK) {
2845 dstream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2846 throw FileNotGoodException("Cannot open database file");
2852 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2853 if(d != SQLITE_OK) {
2854 dstream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2855 throw FileNotGoodException("Cannot prepare read statement");
2858 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2859 if(d != SQLITE_OK) {
2860 dstream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2861 throw FileNotGoodException("Cannot prepare write statement");
2864 dstream<<"Server: Database opened"<<std::endl;
2868 bool ServerMap::loadFromFolders() {
2869 if(!m_database && !fs::PathExists(m_savedir + "/map.sqlite"))
2874 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2875 return (sqlite3_int64)pos.Z*16777216 +
2876 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2879 void ServerMap::createDirs(std::string path)
2881 if(fs::CreateAllDirs(path) == false)
2883 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2884 <<"\""<<path<<"\""<<std::endl;
2885 throw BaseException("ServerMap failed to create directory");
2889 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2895 snprintf(cc, 9, "%.4x%.4x",
2896 (unsigned int)pos.X&0xffff,
2897 (unsigned int)pos.Y&0xffff);
2899 return m_savedir + "/sectors/" + cc;
2901 snprintf(cc, 9, "%.3x/%.3x",
2902 (unsigned int)pos.X&0xfff,
2903 (unsigned int)pos.Y&0xfff);
2905 return m_savedir + "/sectors2/" + cc;
2911 v2s16 ServerMap::getSectorPos(std::string dirname)
2915 size_t spos = dirname.rfind('/') + 1;
2916 assert(spos != std::string::npos);
2917 if(dirname.size() - spos == 8)
2920 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2922 else if(dirname.size() - spos == 3)
2925 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2926 // Sign-extend the 12 bit values up to 16 bits...
2927 if(x&0x800) x|=0xF000;
2928 if(y&0x800) y|=0xF000;
2935 v2s16 pos((s16)x, (s16)y);
2939 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2941 v2s16 p2d = getSectorPos(sectordir);
2943 if(blockfile.size() != 4){
2944 throw InvalidFilenameException("Invalid block filename");
2947 int r = sscanf(blockfile.c_str(), "%4x", &y);
2949 throw InvalidFilenameException("Invalid block filename");
2950 return v3s16(p2d.X, y, p2d.Y);
2953 std::string ServerMap::getBlockFilename(v3s16 p)
2956 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2960 void ServerMap::save(bool only_changed)
2962 DSTACK(__FUNCTION_NAME);
2963 if(m_map_saving_enabled == false)
2965 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2969 if(only_changed == false)
2970 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2973 if(only_changed == false || m_map_metadata_changed)
2978 u32 sector_meta_count = 0;
2979 u32 block_count = 0;
2980 u32 block_count_all = 0; // Number of blocks in memory
2983 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2984 for(; i.atEnd() == false; i++)
2986 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2987 assert(sector->getId() == MAPSECTOR_SERVER);
2989 if(sector->differs_from_disk || only_changed == false)
2991 saveSectorMeta(sector);
2992 sector_meta_count++;
2994 core::list<MapBlock*> blocks;
2995 sector->getBlocks(blocks);
2996 core::list<MapBlock*>::Iterator j;
2998 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2999 for(j=blocks.begin(); j!=blocks.end(); j++)
3001 MapBlock *block = *j;
3005 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
3006 || only_changed == false)
3011 /*dstream<<"ServerMap: Written block ("
3012 <<block->getPos().X<<","
3013 <<block->getPos().Y<<","
3014 <<block->getPos().Z<<")"
3017 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3023 Only print if something happened or saved whole map
3025 if(only_changed == false || sector_meta_count != 0
3026 || block_count != 0)
3028 dstream<<DTIME<<"ServerMap: Written: "
3029 <<sector_meta_count<<" sector metadata files, "
3030 <<block_count<<" block files"
3031 <<", "<<block_count_all<<" blocks in memory."
3036 void ServerMap::saveMapMeta()
3038 DSTACK(__FUNCTION_NAME);
3040 dstream<<"INFO: ServerMap::saveMapMeta(): "
3044 createDirs(m_savedir);
3046 std::string fullpath = m_savedir + "/map_meta.txt";
3047 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3048 if(os.good() == false)
3050 dstream<<"ERROR: ServerMap::saveMapMeta(): "
3051 <<"could not open"<<fullpath<<std::endl;
3052 throw FileNotGoodException("Cannot open chunk metadata");
3056 params.setU64("seed", m_seed);
3058 params.writeLines(os);
3060 os<<"[end_of_params]\n";
3062 m_map_metadata_changed = false;
3065 void ServerMap::loadMapMeta()
3067 DSTACK(__FUNCTION_NAME);
3069 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
3072 std::string fullpath = m_savedir + "/map_meta.txt";
3073 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3074 if(is.good() == false)
3076 dstream<<"ERROR: ServerMap::loadMapMeta(): "
3077 <<"could not open"<<fullpath<<std::endl;
3078 throw FileNotGoodException("Cannot open map metadata");
3086 throw SerializationError
3087 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3089 std::getline(is, line);
3090 std::string trimmedline = trim(line);
3091 if(trimmedline == "[end_of_params]")
3093 params.parseConfigLine(line);
3096 m_seed = params.getU64("seed");
3098 dstream<<"INFO: ServerMap::loadMapMeta(): "
3103 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3105 DSTACK(__FUNCTION_NAME);
3106 // Format used for writing
3107 u8 version = SER_FMT_VER_HIGHEST;
3109 v2s16 pos = sector->getPos();
3110 std::string dir = getSectorDir(pos);
3113 std::string fullpath = dir + "/meta";
3114 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3115 if(o.good() == false)
3116 throw FileNotGoodException("Cannot open sector metafile");
3118 sector->serialize(o, version);
3120 sector->differs_from_disk = false;
3123 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3125 DSTACK(__FUNCTION_NAME);
3127 v2s16 p2d = getSectorPos(sectordir);
3129 ServerMapSector *sector = NULL;
3131 std::string fullpath = sectordir + "/meta";
3132 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3133 if(is.good() == false)
3135 // If the directory exists anyway, it probably is in some old
3136 // format. Just go ahead and create the sector.
3137 if(fs::PathExists(sectordir))
3139 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3140 <<fullpath<<" doesn't exist but directory does."
3141 <<" Continuing with a sector with no metadata."
3143 sector = new ServerMapSector(this, p2d);
3144 m_sectors.insert(p2d, sector);
3148 throw FileNotGoodException("Cannot open sector metafile");
3153 sector = ServerMapSector::deSerialize
3154 (is, this, p2d, m_sectors);
3156 saveSectorMeta(sector);
3159 sector->differs_from_disk = false;
3164 bool ServerMap::loadSectorMeta(v2s16 p2d)
3166 DSTACK(__FUNCTION_NAME);
3168 MapSector *sector = NULL;
3170 // The directory layout we're going to load from.
3171 // 1 - original sectors/xxxxzzzz/
3172 // 2 - new sectors2/xxx/zzz/
3173 // If we load from anything but the latest structure, we will
3174 // immediately save to the new one, and remove the old.
3176 std::string sectordir1 = getSectorDir(p2d, 1);
3177 std::string sectordir;
3178 if(fs::PathExists(sectordir1))
3180 sectordir = sectordir1;
3185 sectordir = getSectorDir(p2d, 2);
3189 sector = loadSectorMeta(sectordir, loadlayout != 2);
3191 catch(InvalidFilenameException &e)
3195 catch(FileNotGoodException &e)
3199 catch(std::exception &e)
3208 bool ServerMap::loadSectorFull(v2s16 p2d)
3210 DSTACK(__FUNCTION_NAME);
3212 MapSector *sector = NULL;
3214 // The directory layout we're going to load from.
3215 // 1 - original sectors/xxxxzzzz/
3216 // 2 - new sectors2/xxx/zzz/
3217 // If we load from anything but the latest structure, we will
3218 // immediately save to the new one, and remove the old.
3220 std::string sectordir1 = getSectorDir(p2d, 1);
3221 std::string sectordir;
3222 if(fs::PathExists(sectordir1))
3224 sectordir = sectordir1;
3229 sectordir = getSectorDir(p2d, 2);
3233 sector = loadSectorMeta(sectordir, loadlayout != 2);
3235 catch(InvalidFilenameException &e)
3239 catch(FileNotGoodException &e)
3243 catch(std::exception &e)
3251 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3253 std::vector<fs::DirListNode>::iterator i2;
3254 for(i2=list2.begin(); i2!=list2.end(); i2++)
3260 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3262 catch(InvalidFilenameException &e)
3264 // This catches unknown crap in directory
3270 dstream<<"Sector converted to new layout - deleting "<<
3271 sectordir1<<std::endl;
3272 fs::RecursiveDelete(sectordir1);
3279 void ServerMap::beginSave() {
3281 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3282 dstream<<"WARNING: beginSave() failed, saving might be slow.";
3285 void ServerMap::endSave() {
3287 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3288 dstream<<"WARNING: endSave() failed, map might not have saved.";
3291 void ServerMap::saveBlock(MapBlock *block)
3293 DSTACK(__FUNCTION_NAME);
3295 Dummy blocks are not written
3297 if(block->isDummy())
3299 /*v3s16 p = block->getPos();
3300 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3301 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3305 // Format used for writing
3306 u8 version = SER_FMT_VER_HIGHEST;
3308 v3s16 p3d = block->getPos();
3312 v2s16 p2d(p3d.X, p3d.Z);
3313 std::string sectordir = getSectorDir(p2d);
3315 createDirs(sectordir);
3317 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3318 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3319 if(o.good() == false)
3320 throw FileNotGoodException("Cannot open block data");
3323 [0] u8 serialization version
3329 std::ostringstream o(std::ios_base::binary);
3331 o.write((char*)&version, 1);
3334 block->serialize(o, version);
3336 // Write extra data stored on disk
3337 block->serializeDiskExtra(o, version);
3339 // Write block to database
3341 std::string tmp = o.str();
3342 const char *bytes = tmp.c_str();
3344 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3345 dstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3346 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3347 dstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3348 int written = sqlite3_step(m_database_write);
3349 if(written != SQLITE_DONE)
3350 dstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3351 <<sqlite3_errmsg(m_database)<<std::endl;
3352 // Make ready for later reuse
3353 sqlite3_reset(m_database_write);
3355 // We just wrote it to the disk so clear modified flag
3356 block->resetModified();
3359 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3361 DSTACK(__FUNCTION_NAME);
3363 std::string fullpath = sectordir+"/"+blockfile;
3366 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3367 if(is.good() == false)
3368 throw FileNotGoodException("Cannot open block file");
3370 v3s16 p3d = getBlockPos(sectordir, blockfile);
3371 v2s16 p2d(p3d.X, p3d.Z);
3373 assert(sector->getPos() == p2d);
3375 u8 version = SER_FMT_VER_INVALID;
3376 is.read((char*)&version, 1);
3379 throw SerializationError("ServerMap::loadBlock(): Failed"
3380 " to read MapBlock version");
3382 /*u32 block_size = MapBlock::serializedLength(version);
3383 SharedBuffer<u8> data(block_size);
3384 is.read((char*)*data, block_size);*/
3386 // This will always return a sector because we're the server
3387 //MapSector *sector = emergeSector(p2d);
3389 MapBlock *block = NULL;
3390 bool created_new = false;
3391 block = sector->getBlockNoCreateNoEx(p3d.Y);
3394 block = sector->createBlankBlockNoInsert(p3d.Y);
3399 block->deSerialize(is, version);
3401 // Read extra data stored on disk
3402 block->deSerializeDiskExtra(is, version);
3404 // If it's a new block, insert it to the map
3406 sector->insertBlock(block);
3409 Save blocks loaded in old format in new format
3412 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3416 // Should be in database now, so delete the old file
3417 fs::RecursiveDelete(fullpath);
3420 // We just loaded it from the disk, so it's up-to-date.
3421 block->resetModified();
3424 catch(SerializationError &e)
3426 dstream<<"WARNING: Invalid block data on disk "
3427 <<"fullpath="<<fullpath
3428 <<" (SerializationError). "
3429 <<"what()="<<e.what()
3431 //" Ignoring. A new one will be generated.
3434 // TODO: Backup file; name is in fullpath.
3438 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3440 DSTACK(__FUNCTION_NAME);
3443 std::istringstream is(*blob, std::ios_base::binary);
3445 u8 version = SER_FMT_VER_INVALID;
3446 is.read((char*)&version, 1);
3449 throw SerializationError("ServerMap::loadBlock(): Failed"
3450 " to read MapBlock version");
3452 /*u32 block_size = MapBlock::serializedLength(version);
3453 SharedBuffer<u8> data(block_size);
3454 is.read((char*)*data, block_size);*/
3456 // This will always return a sector because we're the server
3457 //MapSector *sector = emergeSector(p2d);
3459 MapBlock *block = NULL;
3460 bool created_new = false;
3461 block = sector->getBlockNoCreateNoEx(p3d.Y);
3464 block = sector->createBlankBlockNoInsert(p3d.Y);
3469 block->deSerialize(is, version);
3471 // Read extra data stored on disk
3472 block->deSerializeDiskExtra(is, version);
3474 // If it's a new block, insert it to the map
3476 sector->insertBlock(block);
3479 Save blocks loaded in old format in new format
3482 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3487 // We just loaded it from, so it's up-to-date.
3488 block->resetModified();
3491 catch(SerializationError &e)
3493 dstream<<"WARNING: Invalid block data in database "
3494 <<" (SerializationError). "
3495 <<"what()="<<e.what()
3497 //" Ignoring. A new one will be generated.
3500 // TODO: Copy to a backup database.
3504 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3506 DSTACK(__FUNCTION_NAME);
3508 v2s16 p2d(blockpos.X, blockpos.Z);
3510 if(!loadFromFolders()) {
3513 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3514 dstream<<"WARNING: Could not bind block position for load: "
3515 <<sqlite3_errmsg(m_database)<<std::endl;
3516 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3518 Make sure sector is loaded
3520 MapSector *sector = createSector(p2d);
3525 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3526 size_t len = sqlite3_column_bytes(m_database_read, 0);
3528 std::string datastr(data, len);
3530 loadBlock(&datastr, blockpos, sector, false);
3532 sqlite3_step(m_database_read);
3533 // We should never get more than 1 row, so ok to reset
3534 sqlite3_reset(m_database_read);
3536 return getBlockNoCreateNoEx(blockpos);
3538 sqlite3_reset(m_database_read);
3540 // Not found in database, try the files
3543 // The directory layout we're going to load from.
3544 // 1 - original sectors/xxxxzzzz/
3545 // 2 - new sectors2/xxx/zzz/
3546 // If we load from anything but the latest structure, we will
3547 // immediately save to the new one, and remove the old.
3549 std::string sectordir1 = getSectorDir(p2d, 1);
3550 std::string sectordir;
3551 if(fs::PathExists(sectordir1))
3553 sectordir = sectordir1;
3558 sectordir = getSectorDir(p2d, 2);
3562 Make sure sector is loaded
3564 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3568 sector = loadSectorMeta(sectordir, loadlayout != 2);
3570 catch(InvalidFilenameException &e)
3574 catch(FileNotGoodException &e)
3578 catch(std::exception &e)
3585 Make sure file exists
3588 std::string blockfilename = getBlockFilename(blockpos);
3589 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3593 Load block and save it to the database
3595 loadBlock(sectordir, blockfilename, sector, true);
3596 return getBlockNoCreateNoEx(blockpos);
3599 void ServerMap::PrintInfo(std::ostream &out)
3610 ClientMap::ClientMap(
3612 MapDrawControl &control,
3613 scene::ISceneNode* parent,
3614 scene::ISceneManager* mgr,
3618 scene::ISceneNode(parent, mgr, id),
3621 m_camera_position(0,0,0),
3622 m_camera_direction(0,0,1)
3624 m_camera_mutex.Init();
3625 assert(m_camera_mutex.IsInitialized());
3627 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3628 BS*1000000,BS*1000000,BS*1000000);
3631 ClientMap::~ClientMap()
3633 /*JMutexAutoLock lock(mesh_mutex);
3642 MapSector * ClientMap::emergeSector(v2s16 p2d)
3644 DSTACK(__FUNCTION_NAME);
3645 // Check that it doesn't exist already
3647 return getSectorNoGenerate(p2d);
3649 catch(InvalidPositionException &e)
3654 ClientMapSector *sector = new ClientMapSector(this, p2d);
3657 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3658 m_sectors.insert(p2d, sector);
3665 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3667 DSTACK(__FUNCTION_NAME);
3668 ClientMapSector *sector = NULL;
3670 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3672 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3676 sector = (ClientMapSector*)n->getValue();
3677 assert(sector->getId() == MAPSECTOR_CLIENT);
3681 sector = new ClientMapSector(this, p2d);
3683 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3684 m_sectors.insert(p2d, sector);
3688 sector->deSerialize(is);
3692 void ClientMap::OnRegisterSceneNode()
3696 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3697 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3700 ISceneNode::OnRegisterSceneNode();
3703 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3705 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3706 DSTACK(__FUNCTION_NAME);
3708 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3711 This is called two times per frame, reset on the non-transparent one
3713 if(pass == scene::ESNRP_SOLID)
3715 m_last_drawn_sectors.clear();
3719 Get time for measuring timeout.
3721 Measuring time is very useful for long delays when the
3722 machine is swapping a lot.
3724 int time1 = time(0);
3726 //u32 daynight_ratio = m_client->getDayNightRatio();
3728 m_camera_mutex.Lock();
3729 v3f camera_position = m_camera_position;
3730 v3f camera_direction = m_camera_direction;
3731 m_camera_mutex.Unlock();
3734 Get all blocks and draw all visible ones
3737 v3s16 cam_pos_nodes(
3738 camera_position.X / BS,
3739 camera_position.Y / BS,
3740 camera_position.Z / BS);
3742 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3744 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3745 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3747 // Take a fair amount as we will be dropping more out later
3749 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3750 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3751 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3753 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3754 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3755 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3757 u32 vertex_count = 0;
3759 // For limiting number of mesh updates per frame
3760 u32 mesh_update_count = 0;
3762 u32 blocks_would_have_drawn = 0;
3763 u32 blocks_drawn = 0;
3765 int timecheck_counter = 0;
3766 core::map<v2s16, MapSector*>::Iterator si;
3767 si = m_sectors.getIterator();
3768 for(; si.atEnd() == false; si++)
3771 timecheck_counter++;
3772 if(timecheck_counter > 50)
3774 timecheck_counter = 0;
3775 int time2 = time(0);
3776 if(time2 > time1 + 4)
3778 dstream<<"ClientMap::renderMap(): "
3779 "Rendering takes ages, returning."
3786 MapSector *sector = si.getNode()->getValue();
3787 v2s16 sp = sector->getPos();
3789 if(m_control.range_all == false)
3791 if(sp.X < p_blocks_min.X
3792 || sp.X > p_blocks_max.X
3793 || sp.Y < p_blocks_min.Z
3794 || sp.Y > p_blocks_max.Z)
3798 core::list< MapBlock * > sectorblocks;
3799 sector->getBlocks(sectorblocks);
3805 u32 sector_blocks_drawn = 0;
3807 core::list< MapBlock * >::Iterator i;
3808 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3810 MapBlock *block = *i;
3813 Compare block position to camera position, skip
3814 if not seen on display
3817 float range = 100000 * BS;
3818 if(m_control.range_all == false)
3819 range = m_control.wanted_range * BS;
3822 if(isBlockInSight(block->getPos(), camera_position,
3823 camera_direction, range, &d) == false)
3828 // Okay, this block will be drawn. Reset usage timer.
3829 block->resetUsageTimer();
3831 // This is ugly (spherical distance limit?)
3832 /*if(m_control.range_all == false &&
3833 d - 0.5*BS*MAP_BLOCKSIZE > range)
3838 Update expired mesh (used for day/night change)
3840 It doesn't work exactly like it should now with the
3841 tasked mesh update but whatever.
3844 bool mesh_expired = false;
3847 JMutexAutoLock lock(block->mesh_mutex);
3849 mesh_expired = block->getMeshExpired();
3851 // Mesh has not been expired and there is no mesh:
3852 // block has no content
3853 if(block->mesh == NULL && mesh_expired == false)
3857 f32 faraway = BS*50;
3858 //f32 faraway = m_control.wanted_range * BS;
3861 This has to be done with the mesh_mutex unlocked
3863 // Pretty random but this should work somewhat nicely
3864 if(mesh_expired && (
3865 (mesh_update_count < 3
3866 && (d < faraway || mesh_update_count < 2)
3869 (m_control.range_all && mesh_update_count < 20)
3872 /*if(mesh_expired && mesh_update_count < 6
3873 && (d < faraway || mesh_update_count < 3))*/
3875 mesh_update_count++;
3877 // Mesh has been expired: generate new mesh
3878 //block->updateMesh(daynight_ratio);
3879 m_client->addUpdateMeshTask(block->getPos());
3881 mesh_expired = false;
3886 Draw the faces of the block
3889 JMutexAutoLock lock(block->mesh_mutex);
3891 scene::SMesh *mesh = block->mesh;
3896 blocks_would_have_drawn++;
3897 if(blocks_drawn >= m_control.wanted_max_blocks
3898 && m_control.range_all == false
3899 && d > m_control.wanted_min_range * BS)
3903 sector_blocks_drawn++;
3905 u32 c = mesh->getMeshBufferCount();
3907 for(u32 i=0; i<c; i++)
3909 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3910 const video::SMaterial& material = buf->getMaterial();
3911 video::IMaterialRenderer* rnd =
3912 driver->getMaterialRenderer(material.MaterialType);
3913 bool transparent = (rnd && rnd->isTransparent());
3914 // Render transparent on transparent pass and likewise.
3915 if(transparent == is_transparent_pass)
3918 This *shouldn't* hurt too much because Irrlicht
3919 doesn't change opengl textures if the old
3920 material is set again.
3922 driver->setMaterial(buf->getMaterial());
3923 driver->drawMeshBuffer(buf);
3924 vertex_count += buf->getVertexCount();
3928 } // foreach sectorblocks
3930 if(sector_blocks_drawn != 0)
3932 m_last_drawn_sectors[sp] = true;
3936 m_control.blocks_drawn = blocks_drawn;
3937 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3939 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3940 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3943 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3944 core::map<v3s16, MapBlock*> *affected_blocks)
3946 bool changed = false;
3948 Add it to all blocks touching it
3951 v3s16(0,0,0), // this
3952 v3s16(0,0,1), // back
3953 v3s16(0,1,0), // top
3954 v3s16(1,0,0), // right
3955 v3s16(0,0,-1), // front
3956 v3s16(0,-1,0), // bottom
3957 v3s16(-1,0,0), // left
3959 for(u16 i=0; i<7; i++)
3961 v3s16 p2 = p + dirs[i];
3962 // Block position of neighbor (or requested) node
3963 v3s16 blockpos = getNodeBlockPos(p2);
3964 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3965 if(blockref == NULL)
3967 // Relative position of requested node
3968 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3969 if(blockref->setTempMod(relpos, mod))
3974 if(changed && affected_blocks!=NULL)
3976 for(u16 i=0; i<7; i++)
3978 v3s16 p2 = p + dirs[i];
3979 // Block position of neighbor (or requested) node
3980 v3s16 blockpos = getNodeBlockPos(p2);
3981 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3982 if(blockref == NULL)
3984 affected_blocks->insert(blockpos, blockref);
3990 bool ClientMap::clearTempMod(v3s16 p,
3991 core::map<v3s16, MapBlock*> *affected_blocks)
3993 bool changed = false;
3995 v3s16(0,0,0), // this
3996 v3s16(0,0,1), // back
3997 v3s16(0,1,0), // top
3998 v3s16(1,0,0), // right
3999 v3s16(0,0,-1), // front
4000 v3s16(0,-1,0), // bottom
4001 v3s16(-1,0,0), // left
4003 for(u16 i=0; i<7; i++)
4005 v3s16 p2 = p + dirs[i];
4006 // Block position of neighbor (or requested) node
4007 v3s16 blockpos = getNodeBlockPos(p2);
4008 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4009 if(blockref == NULL)
4011 // Relative position of requested node
4012 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4013 if(blockref->clearTempMod(relpos))
4018 if(changed && affected_blocks!=NULL)
4020 for(u16 i=0; i<7; i++)
4022 v3s16 p2 = p + dirs[i];
4023 // Block position of neighbor (or requested) node
4024 v3s16 blockpos = getNodeBlockPos(p2);
4025 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4026 if(blockref == NULL)
4028 affected_blocks->insert(blockpos, blockref);
4034 void ClientMap::expireMeshes(bool only_daynight_diffed)
4036 TimeTaker timer("expireMeshes()");
4038 core::map<v2s16, MapSector*>::Iterator si;
4039 si = m_sectors.getIterator();
4040 for(; si.atEnd() == false; si++)
4042 MapSector *sector = si.getNode()->getValue();
4044 core::list< MapBlock * > sectorblocks;
4045 sector->getBlocks(sectorblocks);
4047 core::list< MapBlock * >::Iterator i;
4048 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4050 MapBlock *block = *i;
4052 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4058 JMutexAutoLock lock(block->mesh_mutex);
4059 if(block->mesh != NULL)
4061 /*block->mesh->drop();
4062 block->mesh = NULL;*/
4063 block->setMeshExpired(true);
4070 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4072 assert(mapType() == MAPTYPE_CLIENT);
4075 v3s16 p = blockpos + v3s16(0,0,0);
4076 MapBlock *b = getBlockNoCreate(p);
4077 b->updateMesh(daynight_ratio);
4078 //b->setMeshExpired(true);
4080 catch(InvalidPositionException &e){}
4083 v3s16 p = blockpos + v3s16(-1,0,0);
4084 MapBlock *b = getBlockNoCreate(p);
4085 b->updateMesh(daynight_ratio);
4086 //b->setMeshExpired(true);
4088 catch(InvalidPositionException &e){}
4090 v3s16 p = blockpos + v3s16(0,-1,0);
4091 MapBlock *b = getBlockNoCreate(p);
4092 b->updateMesh(daynight_ratio);
4093 //b->setMeshExpired(true);
4095 catch(InvalidPositionException &e){}
4097 v3s16 p = blockpos + v3s16(0,0,-1);
4098 MapBlock *b = getBlockNoCreate(p);
4099 b->updateMesh(daynight_ratio);
4100 //b->setMeshExpired(true);
4102 catch(InvalidPositionException &e){}
4107 Update mesh of block in which the node is, and if the node is at the
4108 leading edge, update the appropriate leading blocks too.
4110 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4118 v3s16 blockposes[4];
4119 for(u32 i=0; i<4; i++)
4121 v3s16 np = nodepos + dirs[i];
4122 blockposes[i] = getNodeBlockPos(np);
4123 // Don't update mesh of block if it has been done already
4124 bool already_updated = false;
4125 for(u32 j=0; j<i; j++)
4127 if(blockposes[j] == blockposes[i])
4129 already_updated = true;
4136 MapBlock *b = getBlockNoCreate(blockposes[i]);
4137 b->updateMesh(daynight_ratio);
4142 void ClientMap::PrintInfo(std::ostream &out)
4153 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4158 MapVoxelManipulator::~MapVoxelManipulator()
4160 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4164 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4166 TimeTaker timer1("emerge", &emerge_time);
4168 // Units of these are MapBlocks
4169 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4170 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4172 VoxelArea block_area_nodes
4173 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4175 addArea(block_area_nodes);
4177 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4178 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4179 for(s32 x=p_min.X; x<=p_max.X; x++)
4182 core::map<v3s16, bool>::Node *n;
4183 n = m_loaded_blocks.find(p);
4187 bool block_data_inexistent = false;
4190 TimeTaker timer1("emerge load", &emerge_load_time);
4192 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4193 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4196 dstream<<std::endl;*/
4198 MapBlock *block = m_map->getBlockNoCreate(p);
4199 if(block->isDummy())
4200 block_data_inexistent = true;
4202 block->copyTo(*this);
4204 catch(InvalidPositionException &e)
4206 block_data_inexistent = true;
4209 if(block_data_inexistent)
4211 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4212 // Fill with VOXELFLAG_INEXISTENT
4213 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4214 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4216 s32 i = m_area.index(a.MinEdge.X,y,z);
4217 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4221 m_loaded_blocks.insert(p, !block_data_inexistent);
4224 //dstream<<"emerge done"<<std::endl;
4228 SUGG: Add an option to only update eg. water and air nodes.
4229 This will make it interfere less with important stuff if
4232 void MapVoxelManipulator::blitBack
4233 (core::map<v3s16, MapBlock*> & modified_blocks)
4235 if(m_area.getExtent() == v3s16(0,0,0))
4238 //TimeTaker timer1("blitBack");
4240 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4241 <<m_loaded_blocks.size()<<std::endl;*/
4244 Initialize block cache
4246 v3s16 blockpos_last;
4247 MapBlock *block = NULL;
4248 bool block_checked_in_modified = false;
4250 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4251 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4252 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4256 u8 f = m_flags[m_area.index(p)];
4257 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4260 MapNode &n = m_data[m_area.index(p)];
4262 v3s16 blockpos = getNodeBlockPos(p);
4267 if(block == NULL || blockpos != blockpos_last){
4268 block = m_map->getBlockNoCreate(blockpos);
4269 blockpos_last = blockpos;
4270 block_checked_in_modified = false;
4273 // Calculate relative position in block
4274 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4276 // Don't continue if nothing has changed here
4277 if(block->getNode(relpos) == n)
4280 //m_map->setNode(m_area.MinEdge + p, n);
4281 block->setNode(relpos, n);
4284 Make sure block is in modified_blocks
4286 if(block_checked_in_modified == false)
4288 modified_blocks[blockpos] = block;
4289 block_checked_in_modified = true;
4292 catch(InvalidPositionException &e)
4298 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4299 MapVoxelManipulator(map),
4300 m_create_area(false)
4304 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4308 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4310 // Just create the area so that it can be pointed to
4311 VoxelManipulator::emerge(a, caller_id);
4314 void ManualMapVoxelManipulator::initialEmerge(
4315 v3s16 blockpos_min, v3s16 blockpos_max)
4317 TimeTaker timer1("initialEmerge", &emerge_time);
4319 // Units of these are MapBlocks
4320 v3s16 p_min = blockpos_min;
4321 v3s16 p_max = blockpos_max;
4323 VoxelArea block_area_nodes
4324 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4326 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4329 dstream<<"initialEmerge: area: ";
4330 block_area_nodes.print(dstream);
4331 dstream<<" ("<<size_MB<<"MB)";
4335 addArea(block_area_nodes);
4337 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4338 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4339 for(s32 x=p_min.X; x<=p_max.X; x++)
4342 core::map<v3s16, bool>::Node *n;
4343 n = m_loaded_blocks.find(p);
4347 bool block_data_inexistent = false;
4350 TimeTaker timer1("emerge load", &emerge_load_time);
4352 MapBlock *block = m_map->getBlockNoCreate(p);
4353 if(block->isDummy())
4354 block_data_inexistent = true;
4356 block->copyTo(*this);
4358 catch(InvalidPositionException &e)
4360 block_data_inexistent = true;
4363 if(block_data_inexistent)
4366 Mark area inexistent
4368 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4369 // Fill with VOXELFLAG_INEXISTENT
4370 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4371 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4373 s32 i = m_area.index(a.MinEdge.X,y,z);
4374 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4378 m_loaded_blocks.insert(p, !block_data_inexistent);
4382 void ManualMapVoxelManipulator::blitBackAll(
4383 core::map<v3s16, MapBlock*> * modified_blocks)
4385 if(m_area.getExtent() == v3s16(0,0,0))
4389 Copy data of all blocks
4391 for(core::map<v3s16, bool>::Iterator
4392 i = m_loaded_blocks.getIterator();
4393 i.atEnd() == false; i++)
4395 v3s16 p = i.getNode()->getKey();
4396 bool existed = i.getNode()->getValue();
4397 if(existed == false)
4399 // The Great Bug was found using this
4400 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4401 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4405 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4408 dstream<<"WARNING: "<<__FUNCTION_NAME
4409 <<": got NULL block "
4410 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4415 block->copyFrom(*this);
4418 modified_blocks->insert(p, block);