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"
32 #include "nodemetadata.h"
33 #include "content_mapnode.h"
35 #include <IMaterialRenderer.h>
43 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
46 SQLite format specification:
47 - Initially only replaces sectors/ and sectors2/
49 If map.sqlite does not exist in the save dir
50 or the block was not found in the database
51 the map will try to load from sectors folder.
52 In either case, map.sqlite will be created
53 and all future saves will save there.
55 Structure of map.sqlite:
66 Map::Map(std::ostream &dout, IGameDef *gamedef):
71 /*m_sector_mutex.Init();
72 assert(m_sector_mutex.IsInitialized());*/
80 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
81 for(; i.atEnd() == false; i++)
83 MapSector *sector = i.getNode()->getValue();
88 void Map::addEventReceiver(MapEventReceiver *event_receiver)
90 m_event_receivers.insert(event_receiver, false);
93 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
95 if(m_event_receivers.find(event_receiver) == NULL)
97 m_event_receivers.remove(event_receiver);
100 void Map::dispatchEvent(MapEditEvent *event)
102 for(core::map<MapEventReceiver*, bool>::Iterator
103 i = m_event_receivers.getIterator();
104 i.atEnd()==false; i++)
106 MapEventReceiver* event_receiver = i.getNode()->getKey();
107 event_receiver->onMapEditEvent(event);
111 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
113 if(m_sector_cache != NULL && p == m_sector_cache_p){
114 MapSector * sector = m_sector_cache;
118 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
123 MapSector *sector = n->getValue();
125 // Cache the last result
126 m_sector_cache_p = p;
127 m_sector_cache = sector;
132 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
134 return getSectorNoGenerateNoExNoLock(p);
137 MapSector * Map::getSectorNoGenerate(v2s16 p)
139 MapSector *sector = getSectorNoGenerateNoEx(p);
141 throw InvalidPositionException();
146 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
148 v2s16 p2d(p3d.X, p3d.Z);
149 MapSector * sector = getSectorNoGenerateNoEx(p2d);
152 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
156 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
158 MapBlock *block = getBlockNoCreateNoEx(p3d);
160 throw InvalidPositionException();
164 bool Map::isNodeUnderground(v3s16 p)
166 v3s16 blockpos = getNodeBlockPos(p);
168 MapBlock * block = getBlockNoCreate(blockpos);
169 return block->getIsUnderground();
171 catch(InvalidPositionException &e)
177 bool Map::isValidPosition(v3s16 p)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreate(blockpos);
181 return (block != NULL);
184 // Returns a CONTENT_IGNORE node if not found
185 MapNode Map::getNodeNoEx(v3s16 p)
187 v3s16 blockpos = getNodeBlockPos(p);
188 MapBlock *block = getBlockNoCreateNoEx(blockpos);
190 return MapNode(CONTENT_IGNORE);
191 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
192 return block->getNodeNoCheck(relpos);
195 // throws InvalidPositionException if not found
196 MapNode Map::getNode(v3s16 p)
198 v3s16 blockpos = getNodeBlockPos(p);
199 MapBlock *block = getBlockNoCreateNoEx(blockpos);
201 throw InvalidPositionException();
202 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
203 return block->getNodeNoCheck(relpos);
206 // throws InvalidPositionException if not found
207 void Map::setNode(v3s16 p, MapNode & n)
209 v3s16 blockpos = getNodeBlockPos(p);
210 MapBlock *block = getBlockNoCreate(blockpos);
211 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
212 block->setNodeNoCheck(relpos, n);
217 Goes recursively through the neighbours of the node.
219 Alters only transparent nodes.
221 If the lighting of the neighbour is lower than the lighting of
222 the node was (before changing it to 0 at the step before), the
223 lighting of the neighbour is set to 0 and then the same stuff
224 repeats for the neighbour.
226 The ending nodes of the routine are stored in light_sources.
227 This is useful when a light is removed. In such case, this
228 routine can be called for the light node and then again for
229 light_sources to re-light the area without the removed light.
231 values of from_nodes are lighting values.
233 void Map::unspreadLight(enum LightBank bank,
234 core::map<v3s16, u8> & from_nodes,
235 core::map<v3s16, bool> & light_sources,
236 core::map<v3s16, MapBlock*> & modified_blocks)
238 INodeDefManager *nodemgr = m_gamedef->ndef();
241 v3s16(0,0,1), // back
243 v3s16(1,0,0), // right
244 v3s16(0,0,-1), // front
245 v3s16(0,-1,0), // bottom
246 v3s16(-1,0,0), // left
249 if(from_nodes.size() == 0)
252 u32 blockchangecount = 0;
254 core::map<v3s16, u8> unlighted_nodes;
255 core::map<v3s16, u8>::Iterator j;
256 j = from_nodes.getIterator();
259 Initialize block cache
262 MapBlock *block = NULL;
263 // Cache this a bit, too
264 bool block_checked_in_modified = false;
266 for(; j.atEnd() == false; j++)
268 v3s16 pos = j.getNode()->getKey();
269 v3s16 blockpos = getNodeBlockPos(pos);
271 // Only fetch a new block if the block position has changed
273 if(block == NULL || blockpos != blockpos_last){
274 block = getBlockNoCreate(blockpos);
275 blockpos_last = blockpos;
277 block_checked_in_modified = false;
281 catch(InvalidPositionException &e)
289 // Calculate relative position in block
290 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
292 // Get node straight from the block
293 MapNode n = block->getNode(relpos);
295 u8 oldlight = j.getNode()->getValue();
297 // Loop through 6 neighbors
298 for(u16 i=0; i<6; i++)
300 // Get the position of the neighbor node
301 v3s16 n2pos = pos + dirs[i];
303 // Get the block where the node is located
304 v3s16 blockpos = getNodeBlockPos(n2pos);
308 // Only fetch a new block if the block position has changed
310 if(block == NULL || blockpos != blockpos_last){
311 block = getBlockNoCreate(blockpos);
312 blockpos_last = blockpos;
314 block_checked_in_modified = false;
318 catch(InvalidPositionException &e)
323 // Calculate relative position in block
324 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
325 // Get node straight from the block
326 MapNode n2 = block->getNode(relpos);
328 bool changed = false;
330 //TODO: Optimize output by optimizing light_sources?
333 If the neighbor is dimmer than what was specified
334 as oldlight (the light of the previous node)
336 if(n2.getLight(bank, nodemgr) < oldlight)
339 And the neighbor is transparent and it has some light
341 if(nodemgr->get(n2).light_propagates
342 && n2.getLight(bank, nodemgr) != 0)
345 Set light to 0 and add to queue
348 u8 current_light = n2.getLight(bank, nodemgr);
349 n2.setLight(bank, 0, nodemgr);
350 block->setNode(relpos, n2);
352 unlighted_nodes.insert(n2pos, current_light);
356 Remove from light_sources if it is there
357 NOTE: This doesn't happen nearly at all
359 /*if(light_sources.find(n2pos))
361 infostream<<"Removed from light_sources"<<std::endl;
362 light_sources.remove(n2pos);
367 if(light_sources.find(n2pos) != NULL)
368 light_sources.remove(n2pos);*/
371 light_sources.insert(n2pos, true);
374 // Add to modified_blocks
375 if(changed == true && block_checked_in_modified == false)
377 // If the block is not found in modified_blocks, add.
378 if(modified_blocks.find(blockpos) == NULL)
380 modified_blocks.insert(blockpos, block);
382 block_checked_in_modified = true;
385 catch(InvalidPositionException &e)
392 /*infostream<<"unspreadLight(): Changed block "
393 <<blockchangecount<<" times"
394 <<" for "<<from_nodes.size()<<" nodes"
397 if(unlighted_nodes.size() > 0)
398 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
402 A single-node wrapper of the above
404 void Map::unLightNeighbors(enum LightBank bank,
405 v3s16 pos, u8 lightwas,
406 core::map<v3s16, bool> & light_sources,
407 core::map<v3s16, MapBlock*> & modified_blocks)
409 core::map<v3s16, u8> from_nodes;
410 from_nodes.insert(pos, lightwas);
412 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
416 Lights neighbors of from_nodes, collects all them and then
419 void Map::spreadLight(enum LightBank bank,
420 core::map<v3s16, bool> & from_nodes,
421 core::map<v3s16, MapBlock*> & modified_blocks)
423 INodeDefManager *nodemgr = m_gamedef->ndef();
425 const v3s16 dirs[6] = {
426 v3s16(0,0,1), // back
428 v3s16(1,0,0), // right
429 v3s16(0,0,-1), // front
430 v3s16(0,-1,0), // bottom
431 v3s16(-1,0,0), // left
434 if(from_nodes.size() == 0)
437 u32 blockchangecount = 0;
439 core::map<v3s16, bool> lighted_nodes;
440 core::map<v3s16, bool>::Iterator j;
441 j = from_nodes.getIterator();
444 Initialize block cache
447 MapBlock *block = NULL;
448 // Cache this a bit, too
449 bool block_checked_in_modified = false;
451 for(; j.atEnd() == false; j++)
452 //for(; j != from_nodes.end(); j++)
454 v3s16 pos = j.getNode()->getKey();
456 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
457 v3s16 blockpos = getNodeBlockPos(pos);
459 // Only fetch a new block if the block position has changed
461 if(block == NULL || blockpos != blockpos_last){
462 block = getBlockNoCreate(blockpos);
463 blockpos_last = blockpos;
465 block_checked_in_modified = false;
469 catch(InvalidPositionException &e)
477 // Calculate relative position in block
478 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
480 // Get node straight from the block
481 MapNode n = block->getNode(relpos);
483 u8 oldlight = n.getLight(bank, nodemgr);
484 u8 newlight = diminish_light(oldlight);
486 // Loop through 6 neighbors
487 for(u16 i=0; i<6; i++){
488 // Get the position of the neighbor node
489 v3s16 n2pos = pos + dirs[i];
491 // Get the block where the node is located
492 v3s16 blockpos = getNodeBlockPos(n2pos);
496 // Only fetch a new block if the block position has changed
498 if(block == NULL || blockpos != blockpos_last){
499 block = getBlockNoCreate(blockpos);
500 blockpos_last = blockpos;
502 block_checked_in_modified = false;
506 catch(InvalidPositionException &e)
511 // Calculate relative position in block
512 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
513 // Get node straight from the block
514 MapNode n2 = block->getNode(relpos);
516 bool changed = false;
518 If the neighbor is brighter than the current node,
519 add to list (it will light up this node on its turn)
521 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
523 lighted_nodes.insert(n2pos, true);
524 //lighted_nodes.push_back(n2pos);
528 If the neighbor is dimmer than how much light this node
529 would spread on it, add to list
531 if(n2.getLight(bank, nodemgr) < newlight)
533 if(nodemgr->get(n2).light_propagates)
535 n2.setLight(bank, newlight, nodemgr);
536 block->setNode(relpos, n2);
537 lighted_nodes.insert(n2pos, true);
538 //lighted_nodes.push_back(n2pos);
543 // Add to modified_blocks
544 if(changed == true && block_checked_in_modified == false)
546 // If the block is not found in modified_blocks, add.
547 if(modified_blocks.find(blockpos) == NULL)
549 modified_blocks.insert(blockpos, block);
551 block_checked_in_modified = true;
554 catch(InvalidPositionException &e)
561 /*infostream<<"spreadLight(): Changed block "
562 <<blockchangecount<<" times"
563 <<" for "<<from_nodes.size()<<" nodes"
566 if(lighted_nodes.size() > 0)
567 spreadLight(bank, lighted_nodes, modified_blocks);
571 A single-node source variation of the above.
573 void Map::lightNeighbors(enum LightBank bank,
575 core::map<v3s16, MapBlock*> & modified_blocks)
577 core::map<v3s16, bool> from_nodes;
578 from_nodes.insert(pos, true);
579 spreadLight(bank, from_nodes, modified_blocks);
582 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
584 INodeDefManager *nodemgr = m_gamedef->ndef();
587 v3s16(0,0,1), // back
589 v3s16(1,0,0), // right
590 v3s16(0,0,-1), // front
591 v3s16(0,-1,0), // bottom
592 v3s16(-1,0,0), // left
595 u8 brightest_light = 0;
596 v3s16 brightest_pos(0,0,0);
597 bool found_something = false;
599 // Loop through 6 neighbors
600 for(u16 i=0; i<6; i++){
601 // Get the position of the neighbor node
602 v3s16 n2pos = p + dirs[i];
607 catch(InvalidPositionException &e)
611 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
612 brightest_light = n2.getLight(bank, nodemgr);
613 brightest_pos = n2pos;
614 found_something = true;
618 if(found_something == false)
619 throw InvalidPositionException();
621 return brightest_pos;
625 Propagates sunlight down from a node.
626 Starting point gets sunlight.
628 Returns the lowest y value of where the sunlight went.
630 Mud is turned into grass in where the sunlight stops.
632 s16 Map::propagateSunlight(v3s16 start,
633 core::map<v3s16, MapBlock*> & modified_blocks)
635 INodeDefManager *nodemgr = m_gamedef->ndef();
640 v3s16 pos(start.X, y, start.Z);
642 v3s16 blockpos = getNodeBlockPos(pos);
645 block = getBlockNoCreate(blockpos);
647 catch(InvalidPositionException &e)
652 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
653 MapNode n = block->getNode(relpos);
655 if(nodemgr->get(n).sunlight_propagates)
657 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
658 block->setNode(relpos, n);
660 modified_blocks.insert(blockpos, block);
664 /*// Turn mud into grass
665 if(n.getContent() == CONTENT_MUD)
667 n.setContent(CONTENT_GRASS);
668 block->setNode(relpos, n);
669 modified_blocks.insert(blockpos, block);
672 // Sunlight goes no further
679 void Map::updateLighting(enum LightBank bank,
680 core::map<v3s16, MapBlock*> & a_blocks,
681 core::map<v3s16, MapBlock*> & modified_blocks)
683 INodeDefManager *nodemgr = m_gamedef->ndef();
685 /*m_dout<<DTIME<<"Map::updateLighting(): "
686 <<a_blocks.size()<<" blocks."<<std::endl;*/
688 //TimeTaker timer("updateLighting");
692 //u32 count_was = modified_blocks.size();
694 core::map<v3s16, MapBlock*> blocks_to_update;
696 core::map<v3s16, bool> light_sources;
698 core::map<v3s16, u8> unlight_from;
700 core::map<v3s16, MapBlock*>::Iterator i;
701 i = a_blocks.getIterator();
702 for(; i.atEnd() == false; i++)
704 MapBlock *block = i.getNode()->getValue();
708 // Don't bother with dummy blocks.
712 v3s16 pos = block->getPos();
713 modified_blocks.insert(pos, block);
715 blocks_to_update.insert(pos, block);
718 Clear all light from block
720 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
721 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
727 MapNode n = block->getNode(v3s16(x,y,z));
728 u8 oldlight = n.getLight(bank, nodemgr);
729 n.setLight(bank, 0, nodemgr);
730 block->setNode(v3s16(x,y,z), n);
732 // Collect borders for unlighting
733 if(x==0 || x == MAP_BLOCKSIZE-1
734 || y==0 || y == MAP_BLOCKSIZE-1
735 || z==0 || z == MAP_BLOCKSIZE-1)
737 v3s16 p_map = p + v3s16(
740 MAP_BLOCKSIZE*pos.Z);
741 unlight_from.insert(p_map, oldlight);
744 catch(InvalidPositionException &e)
747 This would happen when dealing with a
751 infostream<<"updateLighting(): InvalidPositionException"
756 if(bank == LIGHTBANK_DAY)
758 bool bottom_valid = block->propagateSunlight(light_sources);
760 // If bottom is valid, we're done.
764 else if(bank == LIGHTBANK_NIGHT)
766 // For night lighting, sunlight is not propagated
771 // Invalid lighting bank
775 /*infostream<<"Bottom for sunlight-propagated block ("
776 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
779 // Bottom sunlight is not valid; get the block and loop to it
783 block = getBlockNoCreate(pos);
785 catch(InvalidPositionException &e)
794 Enable this to disable proper lighting for speeding up map
795 generation for testing or whatever
798 //if(g_settings->get(""))
800 core::map<v3s16, MapBlock*>::Iterator i;
801 i = blocks_to_update.getIterator();
802 for(; i.atEnd() == false; i++)
804 MapBlock *block = i.getNode()->getValue();
805 v3s16 p = block->getPos();
806 block->setLightingExpired(false);
814 TimeTaker timer("unspreadLight");
815 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
820 u32 diff = modified_blocks.size() - count_was;
821 count_was = modified_blocks.size();
822 infostream<<"unspreadLight modified "<<diff<<std::endl;
826 TimeTaker timer("spreadLight");
827 spreadLight(bank, light_sources, modified_blocks);
832 u32 diff = modified_blocks.size() - count_was;
833 count_was = modified_blocks.size();
834 infostream<<"spreadLight modified "<<diff<<std::endl;
839 //MapVoxelManipulator vmanip(this);
841 // Make a manual voxel manipulator and load all the blocks
842 // that touch the requested blocks
843 ManualMapVoxelManipulator vmanip(this);
844 core::map<v3s16, MapBlock*>::Iterator i;
845 i = blocks_to_update.getIterator();
846 for(; i.atEnd() == false; i++)
848 MapBlock *block = i.getNode()->getValue();
849 v3s16 p = block->getPos();
851 // Add all surrounding blocks
852 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
855 Add all surrounding blocks that have up-to-date lighting
856 NOTE: This doesn't quite do the job (not everything
857 appropriate is lighted)
859 /*for(s16 z=-1; z<=1; z++)
860 for(s16 y=-1; y<=1; y++)
861 for(s16 x=-1; x<=1; x++)
864 MapBlock *block = getBlockNoCreateNoEx(p);
869 if(block->getLightingExpired())
871 vmanip.initialEmerge(p, p);
874 // Lighting of block will be updated completely
875 block->setLightingExpired(false);
879 //TimeTaker timer("unSpreadLight");
880 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
883 //TimeTaker timer("spreadLight");
884 vmanip.spreadLight(bank, light_sources, nodemgr);
887 //TimeTaker timer("blitBack");
888 vmanip.blitBack(modified_blocks);
890 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
894 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
897 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
898 core::map<v3s16, MapBlock*> & modified_blocks)
900 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
901 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
904 Update information about whether day and night light differ
906 for(core::map<v3s16, MapBlock*>::Iterator
907 i = modified_blocks.getIterator();
908 i.atEnd() == false; i++)
910 MapBlock *block = i.getNode()->getValue();
911 block->updateDayNightDiff();
917 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
918 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
920 INodeDefManager *nodemgr = m_gamedef->ndef();
923 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
924 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
927 From this node to nodes underneath:
928 If lighting is sunlight (1.0), unlight neighbours and
933 v3s16 toppos = p + v3s16(0,1,0);
934 v3s16 bottompos = p + v3s16(0,-1,0);
936 bool node_under_sunlight = true;
937 core::map<v3s16, bool> light_sources;
940 If there is a node at top and it doesn't have sunlight,
941 there has not been any sunlight going down.
943 Otherwise there probably is.
946 MapNode topnode = getNode(toppos);
948 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
949 node_under_sunlight = false;
951 catch(InvalidPositionException &e)
957 If the new node is solid and there is grass below, change it to mud
959 if(nodemgr->get(n).walkable == true)
962 MapNode bottomnode = getNode(bottompos);
964 if(bottomnode.getContent() == CONTENT_GRASS
965 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
967 bottomnode.setContent(CONTENT_MUD);
968 setNode(bottompos, bottomnode);
971 catch(InvalidPositionException &e)
979 If the new node is mud and it is under sunlight, change it
982 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
984 n.setContent(CONTENT_GRASS);
989 Remove all light that has come out of this node
992 enum LightBank banks[] =
997 for(s32 i=0; i<2; i++)
999 enum LightBank bank = banks[i];
1001 u8 lightwas = getNode(p).getLight(bank, nodemgr);
1003 // Add the block of the added node to modified_blocks
1004 v3s16 blockpos = getNodeBlockPos(p);
1005 MapBlock * block = getBlockNoCreate(blockpos);
1006 assert(block != NULL);
1007 modified_blocks.insert(blockpos, block);
1009 assert(isValidPosition(p));
1011 // Unlight neighbours of node.
1012 // This means setting light of all consequent dimmer nodes
1014 // This also collects the nodes at the border which will spread
1015 // light again into this.
1016 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1018 n.setLight(bank, 0, nodemgr);
1022 If node lets sunlight through and is under sunlight, it has
1025 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
1027 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
1031 Set the node on the map
1040 NodeMetadata *meta_proto = nodemgr->get(n).initial_metadata;
1043 NodeMetadata *meta = meta_proto->clone(m_gamedef);
1044 meta->setOwner(player_name);
1045 setNodeMetadata(p, meta);
1049 If node is under sunlight and doesn't let sunlight through,
1050 take all sunlighted nodes under it and clear light from them
1051 and from where the light has been spread.
1052 TODO: This could be optimized by mass-unlighting instead
1055 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1059 //m_dout<<DTIME<<"y="<<y<<std::endl;
1060 v3s16 n2pos(p.X, y, p.Z);
1064 n2 = getNode(n2pos);
1066 catch(InvalidPositionException &e)
1071 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1073 unLightNeighbors(LIGHTBANK_DAY,
1074 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1075 light_sources, modified_blocks);
1076 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1084 for(s32 i=0; i<2; i++)
1086 enum LightBank bank = banks[i];
1089 Spread light from all nodes that might be capable of doing so
1091 spreadLight(bank, light_sources, modified_blocks);
1095 Update information about whether day and night light differ
1097 for(core::map<v3s16, MapBlock*>::Iterator
1098 i = modified_blocks.getIterator();
1099 i.atEnd() == false; i++)
1101 MapBlock *block = i.getNode()->getValue();
1102 block->updateDayNightDiff();
1106 Add neighboring liquid nodes and the node itself if it is
1107 liquid (=water node was added) to transform queue.
1110 v3s16(0,0,0), // self
1111 v3s16(0,0,1), // back
1112 v3s16(0,1,0), // top
1113 v3s16(1,0,0), // right
1114 v3s16(0,0,-1), // front
1115 v3s16(0,-1,0), // bottom
1116 v3s16(-1,0,0), // left
1118 for(u16 i=0; i<7; i++)
1123 v3s16 p2 = p + dirs[i];
1125 MapNode n2 = getNode(p2);
1126 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1128 m_transforming_liquid.push_back(p2);
1131 }catch(InvalidPositionException &e)
1139 void Map::removeNodeAndUpdate(v3s16 p,
1140 core::map<v3s16, MapBlock*> &modified_blocks)
1142 INodeDefManager *nodemgr = m_gamedef->ndef();
1144 /*PrintInfo(m_dout);
1145 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1146 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1148 bool node_under_sunlight = true;
1150 v3s16 toppos = p + v3s16(0,1,0);
1152 // Node will be replaced with this
1153 content_t replace_material = CONTENT_AIR;
1156 If there is a node at top and it doesn't have sunlight,
1157 there will be no sunlight going down.
1160 MapNode topnode = getNode(toppos);
1162 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1163 node_under_sunlight = false;
1165 catch(InvalidPositionException &e)
1169 core::map<v3s16, bool> light_sources;
1171 enum LightBank banks[] =
1176 for(s32 i=0; i<2; i++)
1178 enum LightBank bank = banks[i];
1181 Unlight neighbors (in case the node is a light source)
1183 unLightNeighbors(bank, p,
1184 getNode(p).getLight(bank, nodemgr),
1185 light_sources, modified_blocks);
1189 Remove node metadata
1192 removeNodeMetadata(p);
1196 This also clears the lighting.
1200 n.setContent(replace_material);
1203 for(s32 i=0; i<2; i++)
1205 enum LightBank bank = banks[i];
1208 Recalculate lighting
1210 spreadLight(bank, light_sources, modified_blocks);
1213 // Add the block of the removed node to modified_blocks
1214 v3s16 blockpos = getNodeBlockPos(p);
1215 MapBlock * block = getBlockNoCreate(blockpos);
1216 assert(block != NULL);
1217 modified_blocks.insert(blockpos, block);
1220 If the removed node was under sunlight, propagate the
1221 sunlight down from it and then light all neighbors
1222 of the propagated blocks.
1224 if(node_under_sunlight)
1226 s16 ybottom = propagateSunlight(p, modified_blocks);
1227 /*m_dout<<DTIME<<"Node was under sunlight. "
1228 "Propagating sunlight";
1229 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1231 for(; y >= ybottom; y--)
1233 v3s16 p2(p.X, y, p.Z);
1234 /*m_dout<<DTIME<<"lighting neighbors of node ("
1235 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1237 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1242 // Set the lighting of this node to 0
1243 // TODO: Is this needed? Lighting is cleared up there already.
1245 MapNode n = getNode(p);
1246 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1249 catch(InvalidPositionException &e)
1255 for(s32 i=0; i<2; i++)
1257 enum LightBank bank = banks[i];
1259 // Get the brightest neighbour node and propagate light from it
1260 v3s16 n2p = getBrightestNeighbour(bank, p);
1262 MapNode n2 = getNode(n2p);
1263 lightNeighbors(bank, n2p, modified_blocks);
1265 catch(InvalidPositionException &e)
1271 Update information about whether day and night light differ
1273 for(core::map<v3s16, MapBlock*>::Iterator
1274 i = modified_blocks.getIterator();
1275 i.atEnd() == false; i++)
1277 MapBlock *block = i.getNode()->getValue();
1278 block->updateDayNightDiff();
1282 Add neighboring liquid nodes and this node to transform queue.
1283 (it's vital for the node itself to get updated last.)
1286 v3s16(0,0,1), // back
1287 v3s16(0,1,0), // top
1288 v3s16(1,0,0), // right
1289 v3s16(0,0,-1), // front
1290 v3s16(0,-1,0), // bottom
1291 v3s16(-1,0,0), // left
1292 v3s16(0,0,0), // self
1294 for(u16 i=0; i<7; i++)
1299 v3s16 p2 = p + dirs[i];
1301 MapNode n2 = getNode(p2);
1302 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1304 m_transforming_liquid.push_back(p2);
1307 }catch(InvalidPositionException &e)
1313 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1316 event.type = MEET_ADDNODE;
1320 bool succeeded = true;
1322 core::map<v3s16, MapBlock*> modified_blocks;
1323 std::string st = std::string("");
1324 addNodeAndUpdate(p, n, modified_blocks, st);
1326 // Copy modified_blocks to event
1327 for(core::map<v3s16, MapBlock*>::Iterator
1328 i = modified_blocks.getIterator();
1329 i.atEnd()==false; i++)
1331 event.modified_blocks.insert(i.getNode()->getKey(), false);
1334 catch(InvalidPositionException &e){
1338 dispatchEvent(&event);
1343 bool Map::removeNodeWithEvent(v3s16 p)
1346 event.type = MEET_REMOVENODE;
1349 bool succeeded = true;
1351 core::map<v3s16, MapBlock*> modified_blocks;
1352 removeNodeAndUpdate(p, modified_blocks);
1354 // Copy modified_blocks to event
1355 for(core::map<v3s16, MapBlock*>::Iterator
1356 i = modified_blocks.getIterator();
1357 i.atEnd()==false; i++)
1359 event.modified_blocks.insert(i.getNode()->getKey(), false);
1362 catch(InvalidPositionException &e){
1366 dispatchEvent(&event);
1371 bool Map::dayNightDiffed(v3s16 blockpos)
1374 v3s16 p = blockpos + v3s16(0,0,0);
1375 MapBlock *b = getBlockNoCreate(p);
1376 if(b->dayNightDiffed())
1379 catch(InvalidPositionException &e){}
1382 v3s16 p = blockpos + v3s16(-1,0,0);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->dayNightDiffed())
1387 catch(InvalidPositionException &e){}
1389 v3s16 p = blockpos + v3s16(0,-1,0);
1390 MapBlock *b = getBlockNoCreate(p);
1391 if(b->dayNightDiffed())
1394 catch(InvalidPositionException &e){}
1396 v3s16 p = blockpos + v3s16(0,0,-1);
1397 MapBlock *b = getBlockNoCreate(p);
1398 if(b->dayNightDiffed())
1401 catch(InvalidPositionException &e){}
1404 v3s16 p = blockpos + v3s16(1,0,0);
1405 MapBlock *b = getBlockNoCreate(p);
1406 if(b->dayNightDiffed())
1409 catch(InvalidPositionException &e){}
1411 v3s16 p = blockpos + v3s16(0,1,0);
1412 MapBlock *b = getBlockNoCreate(p);
1413 if(b->dayNightDiffed())
1416 catch(InvalidPositionException &e){}
1418 v3s16 p = blockpos + v3s16(0,0,1);
1419 MapBlock *b = getBlockNoCreate(p);
1420 if(b->dayNightDiffed())
1423 catch(InvalidPositionException &e){}
1429 Updates usage timers
1431 void Map::timerUpdate(float dtime, float unload_timeout,
1432 core::list<v3s16> *unloaded_blocks)
1434 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1436 core::list<v2s16> sector_deletion_queue;
1437 u32 deleted_blocks_count = 0;
1438 u32 saved_blocks_count = 0;
1440 core::map<v2s16, MapSector*>::Iterator si;
1443 si = m_sectors.getIterator();
1444 for(; si.atEnd() == false; si++)
1446 MapSector *sector = si.getNode()->getValue();
1448 bool all_blocks_deleted = true;
1450 core::list<MapBlock*> blocks;
1451 sector->getBlocks(blocks);
1453 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1454 i != blocks.end(); i++)
1456 MapBlock *block = (*i);
1458 block->incrementUsageTimer(dtime);
1460 if(block->getUsageTimer() > unload_timeout)
1462 v3s16 p = block->getPos();
1465 if(block->getModified() != MOD_STATE_CLEAN
1466 && save_before_unloading)
1469 saved_blocks_count++;
1472 // Delete from memory
1473 sector->deleteBlock(block);
1476 unloaded_blocks->push_back(p);
1478 deleted_blocks_count++;
1482 all_blocks_deleted = false;
1486 if(all_blocks_deleted)
1488 sector_deletion_queue.push_back(si.getNode()->getKey());
1493 // Finally delete the empty sectors
1494 deleteSectors(sector_deletion_queue);
1496 if(deleted_blocks_count != 0)
1498 PrintInfo(infostream); // ServerMap/ClientMap:
1499 infostream<<"Unloaded "<<deleted_blocks_count
1500 <<" blocks from memory";
1501 if(save_before_unloading)
1502 infostream<<", of which "<<saved_blocks_count<<" were written";
1503 infostream<<"."<<std::endl;
1507 void Map::deleteSectors(core::list<v2s16> &list)
1509 core::list<v2s16>::Iterator j;
1510 for(j=list.begin(); j!=list.end(); j++)
1512 MapSector *sector = m_sectors[*j];
1513 // If sector is in sector cache, remove it from there
1514 if(m_sector_cache == sector)
1515 m_sector_cache = NULL;
1516 // Remove from map and delete
1517 m_sectors.remove(*j);
1523 void Map::unloadUnusedData(float timeout,
1524 core::list<v3s16> *deleted_blocks)
1526 core::list<v2s16> sector_deletion_queue;
1527 u32 deleted_blocks_count = 0;
1528 u32 saved_blocks_count = 0;
1530 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1531 for(; si.atEnd() == false; si++)
1533 MapSector *sector = si.getNode()->getValue();
1535 bool all_blocks_deleted = true;
1537 core::list<MapBlock*> blocks;
1538 sector->getBlocks(blocks);
1539 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1540 i != blocks.end(); i++)
1542 MapBlock *block = (*i);
1544 if(block->getUsageTimer() > timeout)
1547 if(block->getModified() != MOD_STATE_CLEAN)
1550 saved_blocks_count++;
1552 // Delete from memory
1553 sector->deleteBlock(block);
1554 deleted_blocks_count++;
1558 all_blocks_deleted = false;
1562 if(all_blocks_deleted)
1564 sector_deletion_queue.push_back(si.getNode()->getKey());
1568 deleteSectors(sector_deletion_queue);
1570 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1571 <<", of which "<<saved_blocks_count<<" were wr."
1574 //return sector_deletion_queue.getSize();
1575 //return deleted_blocks_count;
1579 void Map::PrintInfo(std::ostream &out)
1584 #define WATER_DROP_BOOST 4
1588 NEIGHBOR_SAME_LEVEL,
1591 struct NodeNeighbor {
1597 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1599 INodeDefManager *nodemgr = m_gamedef->ndef();
1601 DSTACK(__FUNCTION_NAME);
1602 //TimeTaker timer("transformLiquids()");
1605 u32 initial_size = m_transforming_liquid.size();
1607 /*if(initial_size != 0)
1608 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1610 // list of nodes that due to viscosity have not reached their max level height
1611 UniqueQueue<v3s16> must_reflow;
1613 // List of MapBlocks that will require a lighting update (due to lava)
1614 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1616 while(m_transforming_liquid.size() != 0)
1618 // This should be done here so that it is done when continue is used
1619 if(loopcount >= initial_size * 3)
1624 Get a queued transforming liquid node
1626 v3s16 p0 = m_transforming_liquid.pop_front();
1628 MapNode n0 = getNodeNoEx(p0);
1631 Collect information about current node
1633 s8 liquid_level = -1;
1634 u8 liquid_kind = CONTENT_IGNORE;
1635 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1636 switch (liquid_type) {
1638 liquid_level = LIQUID_LEVEL_SOURCE;
1639 liquid_kind = nodemgr->get(n0).liquid_alternative_flowing;
1641 case LIQUID_FLOWING:
1642 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1643 liquid_kind = n0.getContent();
1646 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1647 // continue with the next node.
1648 if (n0.getContent() != CONTENT_AIR)
1650 liquid_kind = CONTENT_AIR;
1655 Collect information about the environment
1657 const v3s16 *dirs = g_6dirs;
1658 NodeNeighbor sources[6]; // surrounding sources
1659 int num_sources = 0;
1660 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1662 NodeNeighbor airs[6]; // surrounding air
1664 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1665 int num_neutrals = 0;
1666 bool flowing_down = false;
1667 for (u16 i = 0; i < 6; i++) {
1668 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1671 nt = NEIGHBOR_UPPER;
1674 nt = NEIGHBOR_LOWER;
1677 v3s16 npos = p0 + dirs[i];
1678 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1679 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1681 if (nb.n.getContent() == CONTENT_AIR) {
1682 airs[num_airs++] = nb;
1683 // if the current node is a water source the neighbor
1684 // should be enqueded for transformation regardless of whether the
1685 // current node changes or not.
1686 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1687 m_transforming_liquid.push_back(npos);
1688 // if the current node happens to be a flowing node, it will start to flow down here.
1689 if (nb.t == NEIGHBOR_LOWER) {
1690 flowing_down = true;
1693 neutrals[num_neutrals++] = nb;
1697 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1698 if (liquid_kind == CONTENT_AIR)
1699 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1700 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1701 neutrals[num_neutrals++] = nb;
1703 sources[num_sources++] = nb;
1706 case LIQUID_FLOWING:
1707 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1708 if (liquid_kind == CONTENT_AIR)
1709 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1710 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1711 neutrals[num_neutrals++] = nb;
1713 flows[num_flows++] = nb;
1714 if (nb.t == NEIGHBOR_LOWER)
1715 flowing_down = true;
1722 decide on the type (and possibly level) of the current node
1724 content_t new_node_content;
1725 s8 new_node_level = -1;
1726 s8 max_node_level = -1;
1727 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1728 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1729 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1730 // it's perfectly safe to use liquid_kind here to determine the new node content.
1731 new_node_content = nodemgr->get(liquid_kind).liquid_alternative_source;
1732 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1733 // liquid_kind is set properly, see above
1734 new_node_content = liquid_kind;
1735 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1737 // no surrounding sources, so get the maximum level that can flow into this node
1738 for (u16 i = 0; i < num_flows; i++) {
1739 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1740 switch (flows[i].t) {
1741 case NEIGHBOR_UPPER:
1742 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1743 max_node_level = LIQUID_LEVEL_MAX;
1744 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1745 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1746 } else if (nb_liquid_level > max_node_level)
1747 max_node_level = nb_liquid_level;
1749 case NEIGHBOR_LOWER:
1751 case NEIGHBOR_SAME_LEVEL:
1752 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1753 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1754 max_node_level = nb_liquid_level - 1;
1760 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1761 if (viscosity > 1 && max_node_level != liquid_level) {
1762 // amount to gain, limited by viscosity
1763 // must be at least 1 in absolute value
1764 s8 level_inc = max_node_level - liquid_level;
1765 if (level_inc < -viscosity || level_inc > viscosity)
1766 new_node_level = liquid_level + level_inc/viscosity;
1767 else if (level_inc < 0)
1768 new_node_level = liquid_level - 1;
1769 else if (level_inc > 0)
1770 new_node_level = liquid_level + 1;
1771 if (new_node_level != max_node_level)
1772 must_reflow.push_back(p0);
1774 new_node_level = max_node_level;
1776 if (new_node_level >= 0)
1777 new_node_content = liquid_kind;
1779 new_node_content = CONTENT_AIR;
1784 check if anything has changed. if not, just continue with the next node.
1786 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1787 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1788 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1794 update the current node
1796 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1797 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1798 // set level to last 3 bits, flowing down bit to 4th bit
1799 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1801 // set the liquid level and flow bit to 0
1802 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1804 n0.setContent(new_node_content);
1806 v3s16 blockpos = getNodeBlockPos(p0);
1807 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1809 modified_blocks.insert(blockpos, block);
1810 // If node emits light, MapBlock requires lighting update
1811 if(nodemgr->get(n0).light_source != 0)
1812 lighting_modified_blocks[block->getPos()] = block;
1816 enqueue neighbors for update if neccessary
1818 switch (nodemgr->get(n0.getContent()).liquid_type) {
1820 case LIQUID_FLOWING:
1821 // make sure source flows into all neighboring nodes
1822 for (u16 i = 0; i < num_flows; i++)
1823 if (flows[i].t != NEIGHBOR_UPPER)
1824 m_transforming_liquid.push_back(flows[i].p);
1825 for (u16 i = 0; i < num_airs; i++)
1826 if (airs[i].t != NEIGHBOR_UPPER)
1827 m_transforming_liquid.push_back(airs[i].p);
1830 // this flow has turned to air; neighboring flows might need to do the same
1831 for (u16 i = 0; i < num_flows; i++)
1832 m_transforming_liquid.push_back(flows[i].p);
1836 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1837 while (must_reflow.size() > 0)
1838 m_transforming_liquid.push_back(must_reflow.pop_front());
1839 updateLighting(lighting_modified_blocks, modified_blocks);
1842 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1844 v3s16 blockpos = getNodeBlockPos(p);
1845 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1846 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1848 infostream<<"Map::getNodeMetadata(): Need to emerge "
1849 <<PP(blockpos)<<std::endl;
1850 block = emergeBlock(blockpos, false);
1854 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1858 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1862 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1864 v3s16 blockpos = getNodeBlockPos(p);
1865 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1866 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1868 infostream<<"Map::setNodeMetadata(): Need to emerge "
1869 <<PP(blockpos)<<std::endl;
1870 block = emergeBlock(blockpos, false);
1874 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1878 block->m_node_metadata->set(p_rel, meta);
1881 void Map::removeNodeMetadata(v3s16 p)
1883 v3s16 blockpos = getNodeBlockPos(p);
1884 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1885 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1888 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1892 block->m_node_metadata->remove(p_rel);
1895 void Map::nodeMetadataStep(float dtime,
1896 core::map<v3s16, MapBlock*> &changed_blocks)
1900 Currently there is no way to ensure that all the necessary
1901 blocks are loaded when this is run. (They might get unloaded)
1902 NOTE: ^- Actually, that might not be so. In a quick test it
1903 reloaded a block with a furnace when I walked back to it from
1906 core::map<v2s16, MapSector*>::Iterator si;
1907 si = m_sectors.getIterator();
1908 for(; si.atEnd() == false; si++)
1910 MapSector *sector = si.getNode()->getValue();
1911 core::list< MapBlock * > sectorblocks;
1912 sector->getBlocks(sectorblocks);
1913 core::list< MapBlock * >::Iterator i;
1914 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1916 MapBlock *block = *i;
1917 bool changed = block->m_node_metadata->step(dtime);
1919 changed_blocks[block->getPos()] = block;
1928 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1929 Map(dout_server, gamedef),
1931 m_map_metadata_changed(true),
1933 m_database_read(NULL),
1934 m_database_write(NULL)
1936 infostream<<__FUNCTION_NAME<<std::endl;
1938 //m_chunksize = 8; // Takes a few seconds
1940 if (g_settings->get("fixed_map_seed").empty())
1942 m_seed = (((u64)(myrand()%0xffff)<<0)
1943 + ((u64)(myrand()%0xffff)<<16)
1944 + ((u64)(myrand()%0xffff)<<32)
1945 + ((u64)(myrand()%0xffff)<<48));
1949 m_seed = g_settings->getU64("fixed_map_seed");
1953 Experimental and debug stuff
1960 Try to load map; if not found, create a new one.
1963 m_savedir = savedir;
1964 m_map_saving_enabled = false;
1968 // If directory exists, check contents and load if possible
1969 if(fs::PathExists(m_savedir))
1971 // If directory is empty, it is safe to save into it.
1972 if(fs::GetDirListing(m_savedir).size() == 0)
1974 infostream<<"Server: Empty save directory is valid."
1976 m_map_saving_enabled = true;
1981 // Load map metadata (seed, chunksize)
1984 catch(FileNotGoodException &e){
1985 infostream<<"WARNING: Could not load map metadata"
1986 //<<" Disabling chunk-based generator."
1992 // Load chunk metadata
1995 catch(FileNotGoodException &e){
1996 infostream<<"WARNING: Could not load chunk metadata."
1997 <<" Disabling chunk-based generator."
2002 /*infostream<<"Server: Successfully loaded chunk "
2003 "metadata and sector (0,0) from "<<savedir<<
2004 ", assuming valid save directory."
2007 infostream<<"Server: Successfully loaded map "
2008 <<"and chunk metadata from "<<savedir
2009 <<", assuming valid save directory."
2012 m_map_saving_enabled = true;
2013 // Map loaded, not creating new one
2017 // If directory doesn't exist, it is safe to save to it
2019 m_map_saving_enabled = true;
2022 catch(std::exception &e)
2024 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2025 <<", exception: "<<e.what()<<std::endl;
2026 infostream<<"Please remove the map or fix it."<<std::endl;
2027 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2030 infostream<<"Initializing new map."<<std::endl;
2032 // Create zero sector
2033 emergeSector(v2s16(0,0));
2035 // Initially write whole map
2039 ServerMap::~ServerMap()
2041 infostream<<__FUNCTION_NAME<<std::endl;
2045 if(m_map_saving_enabled)
2047 // Save only changed parts
2049 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2053 infostream<<"Server: map not saved"<<std::endl;
2056 catch(std::exception &e)
2058 infostream<<"Server: Failed to save map to "<<m_savedir
2059 <<", exception: "<<e.what()<<std::endl;
2063 Close database if it was opened
2066 sqlite3_finalize(m_database_read);
2067 if(m_database_write)
2068 sqlite3_finalize(m_database_write);
2070 sqlite3_close(m_database);
2076 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2077 for(; i.atEnd() == false; i++)
2079 MapChunk *chunk = i.getNode()->getValue();
2085 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2087 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2088 if(enable_mapgen_debug_info)
2089 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2090 <<blockpos.Z<<")"<<std::endl;
2092 // Do nothing if not inside limits (+-1 because of neighbors)
2093 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2094 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2100 data->no_op = false;
2101 data->seed = m_seed;
2102 data->blockpos = blockpos;
2103 data->nodemgr = m_gamedef->ndef();
2106 Create the whole area of this and the neighboring blocks
2109 //TimeTaker timer("initBlockMake() create area");
2111 for(s16 x=-1; x<=1; x++)
2112 for(s16 z=-1; z<=1; z++)
2114 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2115 // Sector metadata is loaded from disk if not already loaded.
2116 ServerMapSector *sector = createSector(sectorpos);
2119 for(s16 y=-1; y<=1; y++)
2121 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2122 //MapBlock *block = createBlock(p);
2123 // 1) get from memory, 2) load from disk
2124 MapBlock *block = emergeBlock(p, false);
2125 // 3) create a blank one
2128 block = createBlock(p);
2131 Block gets sunlight if this is true.
2133 Refer to the map generator heuristics.
2135 bool ug = mapgen::block_is_underground(data->seed, p);
2136 block->setIsUnderground(ug);
2139 // Lighting will not be valid after make_chunk is called
2140 block->setLightingExpired(true);
2141 // Lighting will be calculated
2142 //block->setLightingExpired(false);
2148 Now we have a big empty area.
2150 Make a ManualMapVoxelManipulator that contains this and the
2154 // The area that contains this block and it's neighbors
2155 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2156 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2158 data->vmanip = new ManualMapVoxelManipulator(this);
2159 //data->vmanip->setMap(this);
2163 //TimeTaker timer("initBlockMake() initialEmerge");
2164 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2167 // Data is ready now.
2170 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2171 core::map<v3s16, MapBlock*> &changed_blocks)
2173 v3s16 blockpos = data->blockpos;
2174 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2175 <<blockpos.Z<<")"<<std::endl;*/
2179 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2183 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2185 /*infostream<<"Resulting vmanip:"<<std::endl;
2186 data->vmanip.print(infostream);*/
2189 Blit generated stuff to map
2190 NOTE: blitBackAll adds nearly everything to changed_blocks
2194 //TimeTaker timer("finishBlockMake() blitBackAll");
2195 data->vmanip->blitBackAll(&changed_blocks);
2198 if(enable_mapgen_debug_info)
2199 infostream<<"finishBlockMake: changed_blocks.size()="
2200 <<changed_blocks.size()<<std::endl;
2203 Copy transforming liquid information
2205 while(data->transforming_liquid.size() > 0)
2207 v3s16 p = data->transforming_liquid.pop_front();
2208 m_transforming_liquid.push_back(p);
2214 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2218 Set is_underground flag for lighting with sunlight.
2220 Refer to map generator heuristics.
2222 NOTE: This is done in initChunkMake
2224 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2228 Add sunlight to central block.
2229 This makes in-dark-spawning monsters to not flood the whole thing.
2230 Do not spread the light, though.
2232 /*core::map<v3s16, bool> light_sources;
2233 bool black_air_left = false;
2234 block->propagateSunlight(light_sources, true, &black_air_left);*/
2237 NOTE: Lighting and object adding shouldn't really be here, but
2238 lighting is a bit tricky to move properly to makeBlock.
2239 TODO: Do this the right way anyway, that is, move it to makeBlock.
2240 - There needs to be some way for makeBlock to report back if
2241 the lighting update is going further down because of the
2242 new block blocking light
2247 NOTE: This takes ~60ms, TODO: Investigate why
2250 TimeTaker t("finishBlockMake lighting update");
2252 core::map<v3s16, MapBlock*> lighting_update_blocks;
2255 lighting_update_blocks.insert(block->getPos(), block);
2260 v3s16 p = block->getPos()+v3s16(x,1,z);
2261 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2265 // All modified blocks
2266 // NOTE: Should this be done? If this is not done, then the lighting
2267 // of the others will be updated in a different place, one by one, i
2268 // think... or they might not? Well, at least they are left marked as
2269 // "lighting expired"; it seems that is not handled at all anywhere,
2270 // so enabling this will slow it down A LOT because otherwise it
2271 // would not do this at all. This causes the black trees.
2272 for(core::map<v3s16, MapBlock*>::Iterator
2273 i = changed_blocks.getIterator();
2274 i.atEnd() == false; i++)
2276 lighting_update_blocks.insert(i.getNode()->getKey(),
2277 i.getNode()->getValue());
2279 /*// Also force-add all the upmost blocks for proper sunlight
2280 for(s16 x=-1; x<=1; x++)
2281 for(s16 z=-1; z<=1; z++)
2283 v3s16 p = block->getPos()+v3s16(x,1,z);
2284 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2287 updateLighting(lighting_update_blocks, changed_blocks);
2290 Set lighting to non-expired state in all of them.
2291 This is cheating, but it is not fast enough if all of them
2292 would actually be updated.
2294 for(s16 x=-1; x<=1; x++)
2295 for(s16 y=-1; y<=1; y++)
2296 for(s16 z=-1; z<=1; z++)
2298 v3s16 p = block->getPos()+v3s16(x,y,z);
2299 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2302 if(enable_mapgen_debug_info == false)
2303 t.stop(true); // Hide output
2307 Add random objects to block
2309 mapgen::add_random_objects(block);
2312 Go through changed blocks
2314 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2315 i.atEnd() == false; i++)
2317 MapBlock *block = i.getNode()->getValue();
2320 Update day/night difference cache of the MapBlocks
2322 block->updateDayNightDiff();
2324 Set block as modified
2326 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2330 Set central block as generated
2332 block->setGenerated(true);
2335 Save changed parts of map
2336 NOTE: Will be saved later.
2340 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2341 <<blockpos.Z<<")"<<std::endl;*/
2343 if(enable_mapgen_debug_info)
2346 Analyze resulting blocks
2348 for(s16 x=-1; x<=1; x++)
2349 for(s16 y=-1; y<=1; y++)
2350 for(s16 z=-1; z<=1; z++)
2352 v3s16 p = block->getPos()+v3s16(x,y,z);
2353 MapBlock *block = getBlockNoCreateNoEx(p);
2355 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2356 infostream<<"Generated "<<spos<<": "
2357 <<analyze_block(block)<<std::endl;
2365 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2367 DSTACKF("%s: p2d=(%d,%d)",
2372 Check if it exists already in memory
2374 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2379 Try to load it from disk (with blocks)
2381 //if(loadSectorFull(p2d) == true)
2384 Try to load metadata from disk
2387 if(loadSectorMeta(p2d) == true)
2389 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2392 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2393 throw InvalidPositionException("");
2399 Do not create over-limit
2401 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2402 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2403 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2404 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2405 throw InvalidPositionException("createSector(): pos. over limit");
2408 Generate blank sector
2411 sector = new ServerMapSector(this, p2d, m_gamedef);
2413 // Sector position on map in nodes
2414 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2419 m_sectors.insert(p2d, sector);
2425 This is a quick-hand function for calling makeBlock().
2427 MapBlock * ServerMap::generateBlock(
2429 core::map<v3s16, MapBlock*> &modified_blocks
2432 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2434 /*infostream<<"generateBlock(): "
2435 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2438 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2440 TimeTaker timer("generateBlock");
2442 //MapBlock *block = original_dummy;
2444 v2s16 p2d(p.X, p.Z);
2445 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2448 Do not generate over-limit
2450 if(blockpos_over_limit(p))
2452 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2453 throw InvalidPositionException("generateBlock(): pos. over limit");
2457 Create block make data
2459 mapgen::BlockMakeData data;
2460 initBlockMake(&data, p);
2466 TimeTaker t("mapgen::make_block()");
2467 mapgen::make_block(&data);
2469 if(enable_mapgen_debug_info == false)
2470 t.stop(true); // Hide output
2474 Blit data back on map, update lighting, add mobs and whatever this does
2476 finishBlockMake(&data, modified_blocks);
2481 MapBlock *block = getBlockNoCreateNoEx(p);
2489 bool erroneus_content = false;
2490 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2491 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2492 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2495 MapNode n = block->getNode(p);
2496 if(n.getContent() == CONTENT_IGNORE)
2498 infostream<<"CONTENT_IGNORE at "
2499 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2501 erroneus_content = true;
2505 if(erroneus_content)
2514 Generate a completely empty block
2518 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2519 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2521 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2525 n.setContent(CONTENT_AIR);
2527 n.setContent(CONTENT_STONE);
2528 block->setNode(v3s16(x0,y0,z0), n);
2534 if(enable_mapgen_debug_info == false)
2535 timer.stop(true); // Hide output
2540 MapBlock * ServerMap::createBlock(v3s16 p)
2542 DSTACKF("%s: p=(%d,%d,%d)",
2543 __FUNCTION_NAME, p.X, p.Y, p.Z);
2546 Do not create over-limit
2548 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2549 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2550 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2551 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2552 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2553 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2554 throw InvalidPositionException("createBlock(): pos. over limit");
2556 v2s16 p2d(p.X, p.Z);
2559 This will create or load a sector if not found in memory.
2560 If block exists on disk, it will be loaded.
2562 NOTE: On old save formats, this will be slow, as it generates
2563 lighting on blocks for them.
2565 ServerMapSector *sector;
2567 sector = (ServerMapSector*)createSector(p2d);
2568 assert(sector->getId() == MAPSECTOR_SERVER);
2570 catch(InvalidPositionException &e)
2572 infostream<<"createBlock: createSector() failed"<<std::endl;
2576 NOTE: This should not be done, or at least the exception
2577 should not be passed on as std::exception, because it
2578 won't be catched at all.
2580 /*catch(std::exception &e)
2582 infostream<<"createBlock: createSector() failed: "
2583 <<e.what()<<std::endl;
2588 Try to get a block from the sector
2591 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2594 if(block->isDummy())
2599 block = sector->createBlankBlock(block_y);
2603 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2605 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2607 p.X, p.Y, p.Z, allow_generate);
2610 MapBlock *block = getBlockNoCreateNoEx(p);
2611 if(block && block->isDummy() == false)
2616 MapBlock *block = loadBlock(p);
2623 core::map<v3s16, MapBlock*> modified_blocks;
2624 MapBlock *block = generateBlock(p, modified_blocks);
2628 event.type = MEET_OTHER;
2631 // Copy modified_blocks to event
2632 for(core::map<v3s16, MapBlock*>::Iterator
2633 i = modified_blocks.getIterator();
2634 i.atEnd()==false; i++)
2636 event.modified_blocks.insert(i.getNode()->getKey(), false);
2640 dispatchEvent(&event);
2649 s16 ServerMap::findGroundLevel(v2s16 p2d)
2653 Uh, just do something random...
2655 // Find existing map from top to down
2658 v3s16 p(p2d.X, max, p2d.Y);
2659 for(; p.Y>min; p.Y--)
2661 MapNode n = getNodeNoEx(p);
2662 if(n.getContent() != CONTENT_IGNORE)
2667 // If this node is not air, go to plan b
2668 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2670 // Search existing walkable and return it
2671 for(; p.Y>min; p.Y--)
2673 MapNode n = getNodeNoEx(p);
2674 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2683 Determine from map generator noise functions
2686 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2689 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2690 //return (s16)level;
2693 void ServerMap::createDatabase() {
2696 e = sqlite3_exec(m_database,
2697 "CREATE TABLE IF NOT EXISTS `blocks` ("
2698 "`pos` INT NOT NULL PRIMARY KEY,"
2701 , NULL, NULL, NULL);
2702 if(e == SQLITE_ABORT)
2703 throw FileNotGoodException("Could not create database structure");
2705 infostream<<"Server: Database structure was created";
2708 void ServerMap::verifyDatabase() {
2713 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2714 bool needs_create = false;
2718 Open the database connection
2721 createDirs(m_savedir);
2723 if(!fs::PathExists(dbp))
2724 needs_create = true;
2726 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2727 if(d != SQLITE_OK) {
2728 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2729 throw FileNotGoodException("Cannot open database file");
2735 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2736 if(d != SQLITE_OK) {
2737 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2738 throw FileNotGoodException("Cannot prepare read statement");
2741 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2742 if(d != SQLITE_OK) {
2743 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2744 throw FileNotGoodException("Cannot prepare write statement");
2747 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2748 if(d != SQLITE_OK) {
2749 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2750 throw FileNotGoodException("Cannot prepare read statement");
2753 infostream<<"Server: Database opened"<<std::endl;
2757 bool ServerMap::loadFromFolders() {
2758 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2763 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2764 return (sqlite3_int64)pos.Z*16777216 +
2765 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2768 void ServerMap::createDirs(std::string path)
2770 if(fs::CreateAllDirs(path) == false)
2772 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2773 <<"\""<<path<<"\""<<std::endl;
2774 throw BaseException("ServerMap failed to create directory");
2778 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2784 snprintf(cc, 9, "%.4x%.4x",
2785 (unsigned int)pos.X&0xffff,
2786 (unsigned int)pos.Y&0xffff);
2788 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2790 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2791 (unsigned int)pos.X&0xfff,
2792 (unsigned int)pos.Y&0xfff);
2794 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2800 v2s16 ServerMap::getSectorPos(std::string dirname)
2804 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2805 assert(spos != std::string::npos);
2806 if(dirname.size() - spos == 8)
2809 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2811 else if(dirname.size() - spos == 3)
2814 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2815 // Sign-extend the 12 bit values up to 16 bits...
2816 if(x&0x800) x|=0xF000;
2817 if(y&0x800) y|=0xF000;
2824 v2s16 pos((s16)x, (s16)y);
2828 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2830 v2s16 p2d = getSectorPos(sectordir);
2832 if(blockfile.size() != 4){
2833 throw InvalidFilenameException("Invalid block filename");
2836 int r = sscanf(blockfile.c_str(), "%4x", &y);
2838 throw InvalidFilenameException("Invalid block filename");
2839 return v3s16(p2d.X, y, p2d.Y);
2842 std::string ServerMap::getBlockFilename(v3s16 p)
2845 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2849 void ServerMap::save(bool only_changed)
2851 DSTACK(__FUNCTION_NAME);
2852 if(m_map_saving_enabled == false)
2854 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2858 if(only_changed == false)
2859 infostream<<"ServerMap: Saving whole map, this can take time."
2862 if(only_changed == false || m_map_metadata_changed)
2867 u32 sector_meta_count = 0;
2868 u32 block_count = 0;
2869 u32 block_count_all = 0; // Number of blocks in memory
2872 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2873 for(; i.atEnd() == false; i++)
2875 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2876 assert(sector->getId() == MAPSECTOR_SERVER);
2878 if(sector->differs_from_disk || only_changed == false)
2880 saveSectorMeta(sector);
2881 sector_meta_count++;
2883 core::list<MapBlock*> blocks;
2884 sector->getBlocks(blocks);
2885 core::list<MapBlock*>::Iterator j;
2887 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2888 for(j=blocks.begin(); j!=blocks.end(); j++)
2890 MapBlock *block = *j;
2894 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2895 || only_changed == false)
2900 /*infostream<<"ServerMap: Written block ("
2901 <<block->getPos().X<<","
2902 <<block->getPos().Y<<","
2903 <<block->getPos().Z<<")"
2906 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2912 Only print if something happened or saved whole map
2914 if(only_changed == false || sector_meta_count != 0
2915 || block_count != 0)
2917 infostream<<"ServerMap: Written: "
2918 <<sector_meta_count<<" sector metadata files, "
2919 <<block_count<<" block files"
2920 <<", "<<block_count_all<<" blocks in memory."
2925 static s32 unsignedToSigned(s32 i, s32 max_positive)
2927 if(i < max_positive)
2930 return i - 2*max_positive;
2933 // modulo of a negative number does not work consistently in C
2934 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2938 return mod - ((-i) % mod);
2941 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2943 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2945 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2947 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2948 return v3s16(x,y,z);
2951 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2953 if(loadFromFolders()){
2954 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2955 <<"all blocks that are stored in flat files"<<std::endl;
2961 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2963 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2964 v3s16 p = getIntegerAsBlock(block_i);
2965 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2971 void ServerMap::saveMapMeta()
2973 DSTACK(__FUNCTION_NAME);
2975 infostream<<"ServerMap::saveMapMeta(): "
2979 createDirs(m_savedir);
2981 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2982 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2983 if(os.good() == false)
2985 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2986 <<"could not open"<<fullpath<<std::endl;
2987 throw FileNotGoodException("Cannot open chunk metadata");
2991 params.setU64("seed", m_seed);
2993 params.writeLines(os);
2995 os<<"[end_of_params]\n";
2997 m_map_metadata_changed = false;
3000 void ServerMap::loadMapMeta()
3002 DSTACK(__FUNCTION_NAME);
3004 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3007 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3008 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3009 if(is.good() == false)
3011 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3012 <<"could not open"<<fullpath<<std::endl;
3013 throw FileNotGoodException("Cannot open map metadata");
3021 throw SerializationError
3022 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3024 std::getline(is, line);
3025 std::string trimmedline = trim(line);
3026 if(trimmedline == "[end_of_params]")
3028 params.parseConfigLine(line);
3031 m_seed = params.getU64("seed");
3033 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3036 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3038 DSTACK(__FUNCTION_NAME);
3039 // Format used for writing
3040 u8 version = SER_FMT_VER_HIGHEST;
3042 v2s16 pos = sector->getPos();
3043 std::string dir = getSectorDir(pos);
3046 std::string fullpath = dir + DIR_DELIM + "meta";
3047 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3048 if(o.good() == false)
3049 throw FileNotGoodException("Cannot open sector metafile");
3051 sector->serialize(o, version);
3053 sector->differs_from_disk = false;
3056 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3058 DSTACK(__FUNCTION_NAME);
3060 v2s16 p2d = getSectorPos(sectordir);
3062 ServerMapSector *sector = NULL;
3064 std::string fullpath = sectordir + DIR_DELIM + "meta";
3065 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3066 if(is.good() == false)
3068 // If the directory exists anyway, it probably is in some old
3069 // format. Just go ahead and create the sector.
3070 if(fs::PathExists(sectordir))
3072 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3073 <<fullpath<<" doesn't exist but directory does."
3074 <<" Continuing with a sector with no metadata."
3076 sector = new ServerMapSector(this, p2d, m_gamedef);
3077 m_sectors.insert(p2d, sector);
3081 throw FileNotGoodException("Cannot open sector metafile");
3086 sector = ServerMapSector::deSerialize
3087 (is, this, p2d, m_sectors, m_gamedef);
3089 saveSectorMeta(sector);
3092 sector->differs_from_disk = false;
3097 bool ServerMap::loadSectorMeta(v2s16 p2d)
3099 DSTACK(__FUNCTION_NAME);
3101 MapSector *sector = NULL;
3103 // The directory layout we're going to load from.
3104 // 1 - original sectors/xxxxzzzz/
3105 // 2 - new sectors2/xxx/zzz/
3106 // If we load from anything but the latest structure, we will
3107 // immediately save to the new one, and remove the old.
3109 std::string sectordir1 = getSectorDir(p2d, 1);
3110 std::string sectordir;
3111 if(fs::PathExists(sectordir1))
3113 sectordir = sectordir1;
3118 sectordir = getSectorDir(p2d, 2);
3122 sector = loadSectorMeta(sectordir, loadlayout != 2);
3124 catch(InvalidFilenameException &e)
3128 catch(FileNotGoodException &e)
3132 catch(std::exception &e)
3141 bool ServerMap::loadSectorFull(v2s16 p2d)
3143 DSTACK(__FUNCTION_NAME);
3145 MapSector *sector = NULL;
3147 // The directory layout we're going to load from.
3148 // 1 - original sectors/xxxxzzzz/
3149 // 2 - new sectors2/xxx/zzz/
3150 // If we load from anything but the latest structure, we will
3151 // immediately save to the new one, and remove the old.
3153 std::string sectordir1 = getSectorDir(p2d, 1);
3154 std::string sectordir;
3155 if(fs::PathExists(sectordir1))
3157 sectordir = sectordir1;
3162 sectordir = getSectorDir(p2d, 2);
3166 sector = loadSectorMeta(sectordir, loadlayout != 2);
3168 catch(InvalidFilenameException &e)
3172 catch(FileNotGoodException &e)
3176 catch(std::exception &e)
3184 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3186 std::vector<fs::DirListNode>::iterator i2;
3187 for(i2=list2.begin(); i2!=list2.end(); i2++)
3193 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3195 catch(InvalidFilenameException &e)
3197 // This catches unknown crap in directory
3203 infostream<<"Sector converted to new layout - deleting "<<
3204 sectordir1<<std::endl;
3205 fs::RecursiveDelete(sectordir1);
3212 void ServerMap::beginSave() {
3214 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3215 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3218 void ServerMap::endSave() {
3220 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3221 infostream<<"WARNING: endSave() failed, map might not have saved.";
3224 void ServerMap::saveBlock(MapBlock *block)
3226 DSTACK(__FUNCTION_NAME);
3228 Dummy blocks are not written
3230 if(block->isDummy())
3232 /*v3s16 p = block->getPos();
3233 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3234 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3238 // Format used for writing
3239 u8 version = SER_FMT_VER_HIGHEST;
3241 v3s16 p3d = block->getPos();
3245 v2s16 p2d(p3d.X, p3d.Z);
3246 std::string sectordir = getSectorDir(p2d);
3248 createDirs(sectordir);
3250 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3251 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3252 if(o.good() == false)
3253 throw FileNotGoodException("Cannot open block data");
3256 [0] u8 serialization version
3262 std::ostringstream o(std::ios_base::binary);
3264 o.write((char*)&version, 1);
3267 block->serialize(o, version);
3269 // Write extra data stored on disk
3270 block->serializeDiskExtra(o, version);
3272 // Write block to database
3274 std::string tmp = o.str();
3275 const char *bytes = tmp.c_str();
3277 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3278 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3279 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3280 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3281 int written = sqlite3_step(m_database_write);
3282 if(written != SQLITE_DONE)
3283 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3284 <<sqlite3_errmsg(m_database)<<std::endl;
3285 // Make ready for later reuse
3286 sqlite3_reset(m_database_write);
3288 // We just wrote it to the disk so clear modified flag
3289 block->resetModified();
3292 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3294 DSTACK(__FUNCTION_NAME);
3296 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3299 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3300 if(is.good() == false)
3301 throw FileNotGoodException("Cannot open block file");
3303 v3s16 p3d = getBlockPos(sectordir, blockfile);
3304 v2s16 p2d(p3d.X, p3d.Z);
3306 assert(sector->getPos() == p2d);
3308 u8 version = SER_FMT_VER_INVALID;
3309 is.read((char*)&version, 1);
3312 throw SerializationError("ServerMap::loadBlock(): Failed"
3313 " to read MapBlock version");
3315 /*u32 block_size = MapBlock::serializedLength(version);
3316 SharedBuffer<u8> data(block_size);
3317 is.read((char*)*data, block_size);*/
3319 // This will always return a sector because we're the server
3320 //MapSector *sector = emergeSector(p2d);
3322 MapBlock *block = NULL;
3323 bool created_new = false;
3324 block = sector->getBlockNoCreateNoEx(p3d.Y);
3327 block = sector->createBlankBlockNoInsert(p3d.Y);
3332 block->deSerialize(is, version);
3334 // Read extra data stored on disk
3335 block->deSerializeDiskExtra(is, version);
3337 // If it's a new block, insert it to the map
3339 sector->insertBlock(block);
3342 Save blocks loaded in old format in new format
3345 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3349 // Should be in database now, so delete the old file
3350 fs::RecursiveDelete(fullpath);
3353 // We just loaded it from the disk, so it's up-to-date.
3354 block->resetModified();
3357 catch(SerializationError &e)
3359 infostream<<"WARNING: Invalid block data on disk "
3360 <<"fullpath="<<fullpath
3361 <<" (SerializationError). "
3362 <<"what()="<<e.what()
3364 //" Ignoring. A new one will be generated.
3367 // TODO: Backup file; name is in fullpath.
3371 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3373 DSTACK(__FUNCTION_NAME);
3376 std::istringstream is(*blob, std::ios_base::binary);
3378 u8 version = SER_FMT_VER_INVALID;
3379 is.read((char*)&version, 1);
3382 throw SerializationError("ServerMap::loadBlock(): Failed"
3383 " to read MapBlock version");
3385 /*u32 block_size = MapBlock::serializedLength(version);
3386 SharedBuffer<u8> data(block_size);
3387 is.read((char*)*data, block_size);*/
3389 // This will always return a sector because we're the server
3390 //MapSector *sector = emergeSector(p2d);
3392 MapBlock *block = NULL;
3393 bool created_new = false;
3394 block = sector->getBlockNoCreateNoEx(p3d.Y);
3397 block = sector->createBlankBlockNoInsert(p3d.Y);
3402 block->deSerialize(is, version);
3404 // Read extra data stored on disk
3405 block->deSerializeDiskExtra(is, version);
3407 // If it's a new block, insert it to the map
3409 sector->insertBlock(block);
3412 Save blocks loaded in old format in new format
3415 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3420 // We just loaded it from, so it's up-to-date.
3421 block->resetModified();
3424 catch(SerializationError &e)
3426 infostream<<"WARNING: Invalid block data in database "
3427 <<" (SerializationError). "
3428 <<"what()="<<e.what()
3430 //" Ignoring. A new one will be generated.
3433 // TODO: Copy to a backup database.
3437 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3439 DSTACK(__FUNCTION_NAME);
3441 v2s16 p2d(blockpos.X, blockpos.Z);
3443 if(!loadFromFolders()) {
3446 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3447 infostream<<"WARNING: Could not bind block position for load: "
3448 <<sqlite3_errmsg(m_database)<<std::endl;
3449 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3451 Make sure sector is loaded
3453 MapSector *sector = createSector(p2d);
3458 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3459 size_t len = sqlite3_column_bytes(m_database_read, 0);
3461 std::string datastr(data, len);
3463 loadBlock(&datastr, blockpos, sector, false);
3465 sqlite3_step(m_database_read);
3466 // We should never get more than 1 row, so ok to reset
3467 sqlite3_reset(m_database_read);
3469 return getBlockNoCreateNoEx(blockpos);
3471 sqlite3_reset(m_database_read);
3473 // Not found in database, try the files
3476 // The directory layout we're going to load from.
3477 // 1 - original sectors/xxxxzzzz/
3478 // 2 - new sectors2/xxx/zzz/
3479 // If we load from anything but the latest structure, we will
3480 // immediately save to the new one, and remove the old.
3482 std::string sectordir1 = getSectorDir(p2d, 1);
3483 std::string sectordir;
3484 if(fs::PathExists(sectordir1))
3486 sectordir = sectordir1;
3491 sectordir = getSectorDir(p2d, 2);
3495 Make sure sector is loaded
3497 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3501 sector = loadSectorMeta(sectordir, loadlayout != 2);
3503 catch(InvalidFilenameException &e)
3507 catch(FileNotGoodException &e)
3511 catch(std::exception &e)
3518 Make sure file exists
3521 std::string blockfilename = getBlockFilename(blockpos);
3522 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3526 Load block and save it to the database
3528 loadBlock(sectordir, blockfilename, sector, true);
3529 return getBlockNoCreateNoEx(blockpos);
3532 void ServerMap::PrintInfo(std::ostream &out)
3543 ClientMap::ClientMap(
3546 MapDrawControl &control,
3547 scene::ISceneNode* parent,
3548 scene::ISceneManager* mgr,
3551 Map(dout_client, gamedef),
3552 scene::ISceneNode(parent, mgr, id),
3555 m_camera_position(0,0,0),
3556 m_camera_direction(0,0,1),
3559 m_camera_mutex.Init();
3560 assert(m_camera_mutex.IsInitialized());
3562 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3563 BS*1000000,BS*1000000,BS*1000000);
3566 ClientMap::~ClientMap()
3568 /*JMutexAutoLock lock(mesh_mutex);
3577 MapSector * ClientMap::emergeSector(v2s16 p2d)
3579 DSTACK(__FUNCTION_NAME);
3580 // Check that it doesn't exist already
3582 return getSectorNoGenerate(p2d);
3584 catch(InvalidPositionException &e)
3589 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3592 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3593 m_sectors.insert(p2d, sector);
3600 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3602 DSTACK(__FUNCTION_NAME);
3603 ClientMapSector *sector = NULL;
3605 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3607 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3611 sector = (ClientMapSector*)n->getValue();
3612 assert(sector->getId() == MAPSECTOR_CLIENT);
3616 sector = new ClientMapSector(this, p2d);
3618 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3619 m_sectors.insert(p2d, sector);
3623 sector->deSerialize(is);
3627 void ClientMap::OnRegisterSceneNode()
3631 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3632 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3635 ISceneNode::OnRegisterSceneNode();
3638 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3639 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3641 float d0 = (float)BS * p0.getDistanceFrom(p1);
3643 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3645 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3647 for(float s=start_off; s<d0+end_off; s+=step){
3648 v3f pf = p0f + uf * s;
3649 v3s16 p = floatToInt(pf, BS);
3650 MapNode n = map->getNodeNoEx(p);
3651 bool is_transparent = false;
3652 const ContentFeatures &f = nodemgr->get(n);
3653 if(f.solidness == 0)
3654 is_transparent = (f.visual_solidness != 2);
3656 is_transparent = (f.solidness != 2);
3657 if(!is_transparent){
3659 if(count >= needed_count)
3667 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3669 INodeDefManager *nodemgr = m_gamedef->ndef();
3671 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3672 DSTACK(__FUNCTION_NAME);
3674 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3677 if(pass == scene::ESNRP_SOLID)
3678 prefix = "CM: solid: ";
3680 prefix = "CM: transparent: ";
3683 This is called two times per frame, reset on the non-transparent one
3685 if(pass == scene::ESNRP_SOLID)
3687 m_last_drawn_sectors.clear();
3691 Get time for measuring timeout.
3693 Measuring time is very useful for long delays when the
3694 machine is swapping a lot.
3696 int time1 = time(0);
3698 //u32 daynight_ratio = m_client->getDayNightRatio();
3700 m_camera_mutex.Lock();
3701 v3f camera_position = m_camera_position;
3702 v3f camera_direction = m_camera_direction;
3703 f32 camera_fov = m_camera_fov;
3704 m_camera_mutex.Unlock();
3707 Get all blocks and draw all visible ones
3710 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3712 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3714 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3715 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3717 // Take a fair amount as we will be dropping more out later
3718 // Umm... these additions are a bit strange but they are needed.
3720 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3721 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3722 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3724 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3725 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3726 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3728 u32 vertex_count = 0;
3729 u32 meshbuffer_count = 0;
3731 // For limiting number of mesh updates per frame
3732 u32 mesh_update_count = 0;
3734 // Number of blocks in rendering range
3735 u32 blocks_in_range = 0;
3736 // Number of blocks occlusion culled
3737 u32 blocks_occlusion_culled = 0;
3738 // Number of blocks in rendering range but don't have a mesh
3739 u32 blocks_in_range_without_mesh = 0;
3740 // Blocks that had mesh that would have been drawn according to
3741 // rendering range (if max blocks limit didn't kick in)
3742 u32 blocks_would_have_drawn = 0;
3743 // Blocks that were drawn and had a mesh
3744 u32 blocks_drawn = 0;
3745 // Blocks which had a corresponding meshbuffer for this pass
3746 u32 blocks_had_pass_meshbuf = 0;
3747 // Blocks from which stuff was actually drawn
3748 u32 blocks_without_stuff = 0;
3751 Collect a set of blocks for drawing
3754 core::map<v3s16, MapBlock*> drawset;
3757 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3759 for(core::map<v2s16, MapSector*>::Iterator
3760 si = m_sectors.getIterator();
3761 si.atEnd() == false; si++)
3763 MapSector *sector = si.getNode()->getValue();
3764 v2s16 sp = sector->getPos();
3766 if(m_control.range_all == false)
3768 if(sp.X < p_blocks_min.X
3769 || sp.X > p_blocks_max.X
3770 || sp.Y < p_blocks_min.Z
3771 || sp.Y > p_blocks_max.Z)
3775 core::list< MapBlock * > sectorblocks;
3776 sector->getBlocks(sectorblocks);
3779 Loop through blocks in sector
3782 u32 sector_blocks_drawn = 0;
3784 core::list< MapBlock * >::Iterator i;
3785 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3787 MapBlock *block = *i;
3790 Compare block position to camera position, skip
3791 if not seen on display
3794 float range = 100000 * BS;
3795 if(m_control.range_all == false)
3796 range = m_control.wanted_range * BS;
3799 if(isBlockInSight(block->getPos(), camera_position,
3800 camera_direction, camera_fov,
3801 range, &d) == false)
3806 // This is ugly (spherical distance limit?)
3807 /*if(m_control.range_all == false &&
3808 d - 0.5*BS*MAP_BLOCKSIZE > range)
3815 Update expired mesh (used for day/night change)
3817 It doesn't work exactly like it should now with the
3818 tasked mesh update but whatever.
3821 bool mesh_expired = false;
3824 JMutexAutoLock lock(block->mesh_mutex);
3826 mesh_expired = block->getMeshExpired();
3828 // Mesh has not been expired and there is no mesh:
3829 // block has no content
3830 if(block->mesh == NULL && mesh_expired == false){
3831 blocks_in_range_without_mesh++;
3836 f32 faraway = BS*50;
3837 //f32 faraway = m_control.wanted_range * BS;
3840 This has to be done with the mesh_mutex unlocked
3842 // Pretty random but this should work somewhat nicely
3843 if(mesh_expired && (
3844 (mesh_update_count < 3
3845 && (d < faraway || mesh_update_count < 2)
3848 (m_control.range_all && mesh_update_count < 20)
3851 /*if(mesh_expired && mesh_update_count < 6
3852 && (d < faraway || mesh_update_count < 3))*/
3854 mesh_update_count++;
3856 // Mesh has been expired: generate new mesh
3857 //block->updateMesh(daynight_ratio);
3858 m_client->addUpdateMeshTask(block->getPos());
3860 mesh_expired = false;
3868 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3869 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3871 float stepfac = 1.1;
3872 float startoff = BS*1;
3873 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3874 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3875 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3876 u32 needed_count = 1;
3878 isOccluded(this, spn, cpn + v3s16(0,0,0),
3879 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3880 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3881 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3882 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3883 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3884 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3885 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3886 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3887 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3888 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3889 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3890 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3891 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3892 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3893 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3894 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3895 step, stepfac, startoff, endoff, needed_count, nodemgr)
3898 blocks_occlusion_culled++;
3902 // This block is in range. Reset usage timer.
3903 block->resetUsageTimer();
3906 Ignore if mesh doesn't exist
3909 JMutexAutoLock lock(block->mesh_mutex);
3911 scene::SMesh *mesh = block->mesh;
3914 blocks_in_range_without_mesh++;
3919 // Limit block count in case of a sudden increase
3920 blocks_would_have_drawn++;
3921 if(blocks_drawn >= m_control.wanted_max_blocks
3922 && m_control.range_all == false
3923 && d > m_control.wanted_min_range * BS)
3927 drawset[block->getPos()] = block;
3929 sector_blocks_drawn++;
3932 } // foreach sectorblocks
3934 if(sector_blocks_drawn != 0)
3935 m_last_drawn_sectors[sp] = true;
3940 Draw the selected MapBlocks
3944 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3946 int timecheck_counter = 0;
3947 for(core::map<v3s16, MapBlock*>::Iterator
3948 i = drawset.getIterator();
3949 i.atEnd() == false; i++)
3952 timecheck_counter++;
3953 if(timecheck_counter > 50)
3955 timecheck_counter = 0;
3956 int time2 = time(0);
3957 if(time2 > time1 + 4)
3959 infostream<<"ClientMap::renderMap(): "
3960 "Rendering takes ages, returning."
3967 MapBlock *block = i.getNode()->getValue();
3970 Draw the faces of the block
3973 JMutexAutoLock lock(block->mesh_mutex);
3975 scene::SMesh *mesh = block->mesh;
3978 u32 c = mesh->getMeshBufferCount();
3979 bool stuff_actually_drawn = false;
3980 for(u32 i=0; i<c; i++)
3982 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3983 const video::SMaterial& material = buf->getMaterial();
3984 video::IMaterialRenderer* rnd =
3985 driver->getMaterialRenderer(material.MaterialType);
3986 bool transparent = (rnd && rnd->isTransparent());
3987 // Render transparent on transparent pass and likewise.
3988 if(transparent == is_transparent_pass)
3990 if(buf->getVertexCount() == 0)
3991 errorstream<<"Block ["<<analyze_block(block)
3992 <<"] contains an empty meshbuf"<<std::endl;
3994 This *shouldn't* hurt too much because Irrlicht
3995 doesn't change opengl textures if the old
3996 material has the same texture.
3998 driver->setMaterial(buf->getMaterial());
3999 driver->drawMeshBuffer(buf);
4000 vertex_count += buf->getVertexCount();
4002 stuff_actually_drawn = true;
4005 if(stuff_actually_drawn)
4006 blocks_had_pass_meshbuf++;
4008 blocks_without_stuff++;
4013 // Log only on solid pass because values are the same
4014 if(pass == scene::ESNRP_SOLID){
4015 g_profiler->avg("CM: blocks in range", blocks_in_range);
4016 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
4017 if(blocks_in_range != 0)
4018 g_profiler->avg("CM: blocks in range without mesh (frac)",
4019 (float)blocks_in_range_without_mesh/blocks_in_range);
4020 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4023 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4024 if(blocks_had_pass_meshbuf != 0)
4025 g_profiler->avg(prefix+"meshbuffers per block",
4026 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4027 if(blocks_drawn != 0)
4028 g_profiler->avg(prefix+"empty blocks (frac)",
4029 (float)blocks_without_stuff / blocks_drawn);
4031 m_control.blocks_drawn = blocks_drawn;
4032 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4034 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4035 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4038 void ClientMap::renderPostFx()
4040 INodeDefManager *nodemgr = m_gamedef->ndef();
4042 // Sadly ISceneManager has no "post effects" render pass, in that case we
4043 // could just register for that and handle it in renderMap().
4045 m_camera_mutex.Lock();
4046 v3f camera_position = m_camera_position;
4047 m_camera_mutex.Unlock();
4049 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4051 // - If the player is in a solid node, make everything black.
4052 // - If the player is in liquid, draw a semi-transparent overlay.
4053 const ContentFeatures& features = nodemgr->get(n);
4054 video::SColor post_effect_color = features.post_effect_color;
4055 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4057 post_effect_color = video::SColor(255, 0, 0, 0);
4059 if (post_effect_color.getAlpha() != 0)
4061 // Draw a full-screen rectangle
4062 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4063 v2u32 ss = driver->getScreenSize();
4064 core::rect<s32> rect(0,0, ss.X, ss.Y);
4065 driver->draw2DRectangle(post_effect_color, rect);
4069 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4070 core::map<v3s16, MapBlock*> *affected_blocks)
4072 bool changed = false;
4074 Add it to all blocks touching it
4077 v3s16(0,0,0), // this
4078 v3s16(0,0,1), // back
4079 v3s16(0,1,0), // top
4080 v3s16(1,0,0), // right
4081 v3s16(0,0,-1), // front
4082 v3s16(0,-1,0), // bottom
4083 v3s16(-1,0,0), // left
4085 for(u16 i=0; i<7; i++)
4087 v3s16 p2 = p + dirs[i];
4088 // Block position of neighbor (or requested) node
4089 v3s16 blockpos = getNodeBlockPos(p2);
4090 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4091 if(blockref == NULL)
4093 // Relative position of requested node
4094 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4095 if(blockref->setTempMod(relpos, mod))
4100 if(changed && affected_blocks!=NULL)
4102 for(u16 i=0; i<7; i++)
4104 v3s16 p2 = p + dirs[i];
4105 // Block position of neighbor (or requested) node
4106 v3s16 blockpos = getNodeBlockPos(p2);
4107 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4108 if(blockref == NULL)
4110 affected_blocks->insert(blockpos, blockref);
4116 bool ClientMap::clearTempMod(v3s16 p,
4117 core::map<v3s16, MapBlock*> *affected_blocks)
4119 bool changed = false;
4121 v3s16(0,0,0), // this
4122 v3s16(0,0,1), // back
4123 v3s16(0,1,0), // top
4124 v3s16(1,0,0), // right
4125 v3s16(0,0,-1), // front
4126 v3s16(0,-1,0), // bottom
4127 v3s16(-1,0,0), // left
4129 for(u16 i=0; i<7; i++)
4131 v3s16 p2 = p + dirs[i];
4132 // Block position of neighbor (or requested) node
4133 v3s16 blockpos = getNodeBlockPos(p2);
4134 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4135 if(blockref == NULL)
4137 // Relative position of requested node
4138 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4139 if(blockref->clearTempMod(relpos))
4144 if(changed && affected_blocks!=NULL)
4146 for(u16 i=0; i<7; i++)
4148 v3s16 p2 = p + dirs[i];
4149 // Block position of neighbor (or requested) node
4150 v3s16 blockpos = getNodeBlockPos(p2);
4151 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4152 if(blockref == NULL)
4154 affected_blocks->insert(blockpos, blockref);
4160 void ClientMap::expireMeshes(bool only_daynight_diffed)
4162 TimeTaker timer("expireMeshes()");
4164 core::map<v2s16, MapSector*>::Iterator si;
4165 si = m_sectors.getIterator();
4166 for(; si.atEnd() == false; si++)
4168 MapSector *sector = si.getNode()->getValue();
4170 core::list< MapBlock * > sectorblocks;
4171 sector->getBlocks(sectorblocks);
4173 core::list< MapBlock * >::Iterator i;
4174 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4176 MapBlock *block = *i;
4178 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4184 JMutexAutoLock lock(block->mesh_mutex);
4185 if(block->mesh != NULL)
4187 /*block->mesh->drop();
4188 block->mesh = NULL;*/
4189 block->setMeshExpired(true);
4196 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4198 assert(mapType() == MAPTYPE_CLIENT);
4201 v3s16 p = blockpos + v3s16(0,0,0);
4202 MapBlock *b = getBlockNoCreate(p);
4203 b->updateMesh(daynight_ratio);
4204 //b->setMeshExpired(true);
4206 catch(InvalidPositionException &e){}
4209 v3s16 p = blockpos + v3s16(-1,0,0);
4210 MapBlock *b = getBlockNoCreate(p);
4211 b->updateMesh(daynight_ratio);
4212 //b->setMeshExpired(true);
4214 catch(InvalidPositionException &e){}
4216 v3s16 p = blockpos + v3s16(0,-1,0);
4217 MapBlock *b = getBlockNoCreate(p);
4218 b->updateMesh(daynight_ratio);
4219 //b->setMeshExpired(true);
4221 catch(InvalidPositionException &e){}
4223 v3s16 p = blockpos + v3s16(0,0,-1);
4224 MapBlock *b = getBlockNoCreate(p);
4225 b->updateMesh(daynight_ratio);
4226 //b->setMeshExpired(true);
4228 catch(InvalidPositionException &e){}
4233 Update mesh of block in which the node is, and if the node is at the
4234 leading edge, update the appropriate leading blocks too.
4236 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4244 v3s16 blockposes[4];
4245 for(u32 i=0; i<4; i++)
4247 v3s16 np = nodepos + dirs[i];
4248 blockposes[i] = getNodeBlockPos(np);
4249 // Don't update mesh of block if it has been done already
4250 bool already_updated = false;
4251 for(u32 j=0; j<i; j++)
4253 if(blockposes[j] == blockposes[i])
4255 already_updated = true;
4262 MapBlock *b = getBlockNoCreate(blockposes[i]);
4263 b->updateMesh(daynight_ratio);
4268 void ClientMap::PrintInfo(std::ostream &out)
4279 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4284 MapVoxelManipulator::~MapVoxelManipulator()
4286 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4290 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4292 TimeTaker timer1("emerge", &emerge_time);
4294 // Units of these are MapBlocks
4295 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4296 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4298 VoxelArea block_area_nodes
4299 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4301 addArea(block_area_nodes);
4303 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4304 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4305 for(s32 x=p_min.X; x<=p_max.X; x++)
4308 core::map<v3s16, bool>::Node *n;
4309 n = m_loaded_blocks.find(p);
4313 bool block_data_inexistent = false;
4316 TimeTaker timer1("emerge load", &emerge_load_time);
4318 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4319 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4321 a.print(infostream);
4322 infostream<<std::endl;*/
4324 MapBlock *block = m_map->getBlockNoCreate(p);
4325 if(block->isDummy())
4326 block_data_inexistent = true;
4328 block->copyTo(*this);
4330 catch(InvalidPositionException &e)
4332 block_data_inexistent = true;
4335 if(block_data_inexistent)
4337 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4338 // Fill with VOXELFLAG_INEXISTENT
4339 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4340 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4342 s32 i = m_area.index(a.MinEdge.X,y,z);
4343 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4347 m_loaded_blocks.insert(p, !block_data_inexistent);
4350 //infostream<<"emerge done"<<std::endl;
4354 SUGG: Add an option to only update eg. water and air nodes.
4355 This will make it interfere less with important stuff if
4358 void MapVoxelManipulator::blitBack
4359 (core::map<v3s16, MapBlock*> & modified_blocks)
4361 if(m_area.getExtent() == v3s16(0,0,0))
4364 //TimeTaker timer1("blitBack");
4366 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4367 <<m_loaded_blocks.size()<<std::endl;*/
4370 Initialize block cache
4372 v3s16 blockpos_last;
4373 MapBlock *block = NULL;
4374 bool block_checked_in_modified = false;
4376 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4377 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4378 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4382 u8 f = m_flags[m_area.index(p)];
4383 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4386 MapNode &n = m_data[m_area.index(p)];
4388 v3s16 blockpos = getNodeBlockPos(p);
4393 if(block == NULL || blockpos != blockpos_last){
4394 block = m_map->getBlockNoCreate(blockpos);
4395 blockpos_last = blockpos;
4396 block_checked_in_modified = false;
4399 // Calculate relative position in block
4400 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4402 // Don't continue if nothing has changed here
4403 if(block->getNode(relpos) == n)
4406 //m_map->setNode(m_area.MinEdge + p, n);
4407 block->setNode(relpos, n);
4410 Make sure block is in modified_blocks
4412 if(block_checked_in_modified == false)
4414 modified_blocks[blockpos] = block;
4415 block_checked_in_modified = true;
4418 catch(InvalidPositionException &e)
4424 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4425 MapVoxelManipulator(map),
4426 m_create_area(false)
4430 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4434 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4436 // Just create the area so that it can be pointed to
4437 VoxelManipulator::emerge(a, caller_id);
4440 void ManualMapVoxelManipulator::initialEmerge(
4441 v3s16 blockpos_min, v3s16 blockpos_max)
4443 TimeTaker timer1("initialEmerge", &emerge_time);
4445 // Units of these are MapBlocks
4446 v3s16 p_min = blockpos_min;
4447 v3s16 p_max = blockpos_max;
4449 VoxelArea block_area_nodes
4450 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4452 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4455 infostream<<"initialEmerge: area: ";
4456 block_area_nodes.print(infostream);
4457 infostream<<" ("<<size_MB<<"MB)";
4458 infostream<<std::endl;
4461 addArea(block_area_nodes);
4463 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4464 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4465 for(s32 x=p_min.X; x<=p_max.X; x++)
4468 core::map<v3s16, bool>::Node *n;
4469 n = m_loaded_blocks.find(p);
4473 bool block_data_inexistent = false;
4476 TimeTaker timer1("emerge load", &emerge_load_time);
4478 MapBlock *block = m_map->getBlockNoCreate(p);
4479 if(block->isDummy())
4480 block_data_inexistent = true;
4482 block->copyTo(*this);
4484 catch(InvalidPositionException &e)
4486 block_data_inexistent = true;
4489 if(block_data_inexistent)
4492 Mark area inexistent
4494 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4495 // Fill with VOXELFLAG_INEXISTENT
4496 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4497 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4499 s32 i = m_area.index(a.MinEdge.X,y,z);
4500 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4504 m_loaded_blocks.insert(p, !block_data_inexistent);
4508 void ManualMapVoxelManipulator::blitBackAll(
4509 core::map<v3s16, MapBlock*> * modified_blocks)
4511 if(m_area.getExtent() == v3s16(0,0,0))
4515 Copy data of all blocks
4517 for(core::map<v3s16, bool>::Iterator
4518 i = m_loaded_blocks.getIterator();
4519 i.atEnd() == false; i++)
4521 v3s16 p = i.getNode()->getKey();
4522 bool existed = i.getNode()->getValue();
4523 if(existed == false)
4525 // The Great Bug was found using this
4526 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4527 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4531 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4534 infostream<<"WARNING: "<<__FUNCTION_NAME
4535 <<": got NULL block "
4536 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4541 block->copyFrom(*this);
4544 modified_blocks->insert(p, block);