3 Copyright (C) 2010 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.
22 #include "jmutexautolock.h"
30 #include "serverobject.h"
36 Map::Map(std::ostream &dout):
40 m_sector_mutex.Init();
41 assert(m_sector_mutex.IsInitialized());
49 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
50 for(; i.atEnd() == false; i++)
52 MapSector *sector = i.getNode()->getValue();
57 void Map::addEventReceiver(MapEventReceiver *event_receiver)
59 m_event_receivers.insert(event_receiver, false);
62 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
64 if(m_event_receivers.find(event_receiver) == NULL)
66 m_event_receivers.remove(event_receiver);
69 void Map::dispatchEvent(MapEditEvent *event)
71 for(core::map<MapEventReceiver*, bool>::Iterator
72 i = m_event_receivers.getIterator();
73 i.atEnd()==false; i++)
75 MapEventReceiver* event_receiver = i.getNode()->getKey();
76 event_receiver->onMapEditEvent(event);
80 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
82 if(m_sector_cache != NULL && p == m_sector_cache_p){
83 MapSector * sector = m_sector_cache;
84 // Reset inactivity timer
85 sector->usage_timer = 0.0;
89 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
94 MapSector *sector = n->getValue();
96 // Cache the last result
98 m_sector_cache = sector;
100 // Reset inactivity timer
101 sector->usage_timer = 0.0;
105 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
107 JMutexAutoLock lock(m_sector_mutex);
109 return getSectorNoGenerateNoExNoLock(p);
112 MapSector * Map::getSectorNoGenerate(v2s16 p)
114 MapSector *sector = getSectorNoGenerateNoEx(p);
116 throw InvalidPositionException();
121 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
123 v2s16 p2d(p3d.X, p3d.Z);
124 MapSector * sector = getSectorNoGenerate(p2d);
126 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
131 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
135 v2s16 p2d(p3d.X, p3d.Z);
136 MapSector * sector = getSectorNoGenerate(p2d);
137 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
140 catch(InvalidPositionException &e)
146 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
148 v2s16 p2d(p3d.X, p3d.Z);
149 MapSector * sector = getSectorCreate(p2d);
151 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
154 block = sector->createBlankBlock(p3d.Y);
158 bool Map::isNodeUnderground(v3s16 p)
160 v3s16 blockpos = getNodeBlockPos(p);
162 MapBlock * block = getBlockNoCreate(blockpos);
163 return block->getIsUnderground();
165 catch(InvalidPositionException &e)
172 Goes recursively through the neighbours of the node.
174 Alters only transparent nodes.
176 If the lighting of the neighbour is lower than the lighting of
177 the node was (before changing it to 0 at the step before), the
178 lighting of the neighbour is set to 0 and then the same stuff
179 repeats for the neighbour.
181 The ending nodes of the routine are stored in light_sources.
182 This is useful when a light is removed. In such case, this
183 routine can be called for the light node and then again for
184 light_sources to re-light the area without the removed light.
186 values of from_nodes are lighting values.
188 void Map::unspreadLight(enum LightBank bank,
189 core::map<v3s16, u8> & from_nodes,
190 core::map<v3s16, bool> & light_sources,
191 core::map<v3s16, MapBlock*> & modified_blocks)
194 v3s16(0,0,1), // back
196 v3s16(1,0,0), // right
197 v3s16(0,0,-1), // front
198 v3s16(0,-1,0), // bottom
199 v3s16(-1,0,0), // left
202 if(from_nodes.size() == 0)
205 u32 blockchangecount = 0;
207 core::map<v3s16, u8> unlighted_nodes;
208 core::map<v3s16, u8>::Iterator j;
209 j = from_nodes.getIterator();
212 Initialize block cache
215 MapBlock *block = NULL;
216 // Cache this a bit, too
217 bool block_checked_in_modified = false;
219 for(; j.atEnd() == false; j++)
221 v3s16 pos = j.getNode()->getKey();
222 v3s16 blockpos = getNodeBlockPos(pos);
224 // Only fetch a new block if the block position has changed
226 if(block == NULL || blockpos != blockpos_last){
227 block = getBlockNoCreate(blockpos);
228 blockpos_last = blockpos;
230 block_checked_in_modified = false;
234 catch(InvalidPositionException &e)
242 // Calculate relative position in block
243 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
245 // Get node straight from the block
246 MapNode n = block->getNode(relpos);
248 u8 oldlight = j.getNode()->getValue();
250 // Loop through 6 neighbors
251 for(u16 i=0; i<6; i++)
253 // Get the position of the neighbor node
254 v3s16 n2pos = pos + dirs[i];
256 // Get the block where the node is located
257 v3s16 blockpos = getNodeBlockPos(n2pos);
261 // Only fetch a new block if the block position has changed
263 if(block == NULL || blockpos != blockpos_last){
264 block = getBlockNoCreate(blockpos);
265 blockpos_last = blockpos;
267 block_checked_in_modified = false;
271 catch(InvalidPositionException &e)
276 // Calculate relative position in block
277 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
278 // Get node straight from the block
279 MapNode n2 = block->getNode(relpos);
281 bool changed = false;
283 //TODO: Optimize output by optimizing light_sources?
286 If the neighbor is dimmer than what was specified
287 as oldlight (the light of the previous node)
289 if(n2.getLight(bank) < oldlight)
292 And the neighbor is transparent and it has some light
294 if(n2.light_propagates() && n2.getLight(bank) != 0)
297 Set light to 0 and add to queue
300 u8 current_light = n2.getLight(bank);
301 n2.setLight(bank, 0);
302 block->setNode(relpos, n2);
304 unlighted_nodes.insert(n2pos, current_light);
308 Remove from light_sources if it is there
309 NOTE: This doesn't happen nearly at all
311 /*if(light_sources.find(n2pos))
313 std::cout<<"Removed from light_sources"<<std::endl;
314 light_sources.remove(n2pos);
319 if(light_sources.find(n2pos) != NULL)
320 light_sources.remove(n2pos);*/
323 light_sources.insert(n2pos, true);
326 // Add to modified_blocks
327 if(changed == true && block_checked_in_modified == false)
329 // If the block is not found in modified_blocks, add.
330 if(modified_blocks.find(blockpos) == NULL)
332 modified_blocks.insert(blockpos, block);
334 block_checked_in_modified = true;
337 catch(InvalidPositionException &e)
344 /*dstream<<"unspreadLight(): Changed block "
345 <<blockchangecount<<" times"
346 <<" for "<<from_nodes.size()<<" nodes"
349 if(unlighted_nodes.size() > 0)
350 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
354 A single-node wrapper of the above
356 void Map::unLightNeighbors(enum LightBank bank,
357 v3s16 pos, u8 lightwas,
358 core::map<v3s16, bool> & light_sources,
359 core::map<v3s16, MapBlock*> & modified_blocks)
361 core::map<v3s16, u8> from_nodes;
362 from_nodes.insert(pos, lightwas);
364 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
368 Lights neighbors of from_nodes, collects all them and then
371 void Map::spreadLight(enum LightBank bank,
372 core::map<v3s16, bool> & from_nodes,
373 core::map<v3s16, MapBlock*> & modified_blocks)
375 const v3s16 dirs[6] = {
376 v3s16(0,0,1), // back
378 v3s16(1,0,0), // right
379 v3s16(0,0,-1), // front
380 v3s16(0,-1,0), // bottom
381 v3s16(-1,0,0), // left
384 if(from_nodes.size() == 0)
387 u32 blockchangecount = 0;
389 core::map<v3s16, bool> lighted_nodes;
390 core::map<v3s16, bool>::Iterator j;
391 j = from_nodes.getIterator();
394 Initialize block cache
397 MapBlock *block = NULL;
398 // Cache this a bit, too
399 bool block_checked_in_modified = false;
401 for(; j.atEnd() == false; j++)
402 //for(; j != from_nodes.end(); j++)
404 v3s16 pos = j.getNode()->getKey();
406 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
407 v3s16 blockpos = getNodeBlockPos(pos);
409 // Only fetch a new block if the block position has changed
411 if(block == NULL || blockpos != blockpos_last){
412 block = getBlockNoCreate(blockpos);
413 blockpos_last = blockpos;
415 block_checked_in_modified = false;
419 catch(InvalidPositionException &e)
427 // Calculate relative position in block
428 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
430 // Get node straight from the block
431 MapNode n = block->getNode(relpos);
433 u8 oldlight = n.getLight(bank);
434 u8 newlight = diminish_light(oldlight);
436 // Loop through 6 neighbors
437 for(u16 i=0; i<6; i++){
438 // Get the position of the neighbor node
439 v3s16 n2pos = pos + dirs[i];
441 // Get the block where the node is located
442 v3s16 blockpos = getNodeBlockPos(n2pos);
446 // Only fetch a new block if the block position has changed
448 if(block == NULL || blockpos != blockpos_last){
449 block = getBlockNoCreate(blockpos);
450 blockpos_last = blockpos;
452 block_checked_in_modified = false;
456 catch(InvalidPositionException &e)
461 // Calculate relative position in block
462 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
463 // Get node straight from the block
464 MapNode n2 = block->getNode(relpos);
466 bool changed = false;
468 If the neighbor is brighter than the current node,
469 add to list (it will light up this node on its turn)
471 if(n2.getLight(bank) > undiminish_light(oldlight))
473 lighted_nodes.insert(n2pos, true);
474 //lighted_nodes.push_back(n2pos);
478 If the neighbor is dimmer than how much light this node
479 would spread on it, add to list
481 if(n2.getLight(bank) < newlight)
483 if(n2.light_propagates())
485 n2.setLight(bank, newlight);
486 block->setNode(relpos, n2);
487 lighted_nodes.insert(n2pos, true);
488 //lighted_nodes.push_back(n2pos);
493 // Add to modified_blocks
494 if(changed == true && block_checked_in_modified == false)
496 // If the block is not found in modified_blocks, add.
497 if(modified_blocks.find(blockpos) == NULL)
499 modified_blocks.insert(blockpos, block);
501 block_checked_in_modified = true;
504 catch(InvalidPositionException &e)
511 /*dstream<<"spreadLight(): Changed block "
512 <<blockchangecount<<" times"
513 <<" for "<<from_nodes.size()<<" nodes"
516 if(lighted_nodes.size() > 0)
517 spreadLight(bank, lighted_nodes, modified_blocks);
521 A single-node source variation of the above.
523 void Map::lightNeighbors(enum LightBank bank,
525 core::map<v3s16, MapBlock*> & modified_blocks)
527 core::map<v3s16, bool> from_nodes;
528 from_nodes.insert(pos, true);
529 spreadLight(bank, from_nodes, modified_blocks);
532 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
535 v3s16(0,0,1), // back
537 v3s16(1,0,0), // right
538 v3s16(0,0,-1), // front
539 v3s16(0,-1,0), // bottom
540 v3s16(-1,0,0), // left
543 u8 brightest_light = 0;
544 v3s16 brightest_pos(0,0,0);
545 bool found_something = false;
547 // Loop through 6 neighbors
548 for(u16 i=0; i<6; i++){
549 // Get the position of the neighbor node
550 v3s16 n2pos = p + dirs[i];
555 catch(InvalidPositionException &e)
559 if(n2.getLight(bank) > brightest_light || found_something == false){
560 brightest_light = n2.getLight(bank);
561 brightest_pos = n2pos;
562 found_something = true;
566 if(found_something == false)
567 throw InvalidPositionException();
569 return brightest_pos;
573 Propagates sunlight down from a node.
574 Starting point gets sunlight.
576 Returns the lowest y value of where the sunlight went.
578 Mud is turned into grass in where the sunlight stops.
580 s16 Map::propagateSunlight(v3s16 start,
581 core::map<v3s16, MapBlock*> & modified_blocks)
586 v3s16 pos(start.X, y, start.Z);
588 v3s16 blockpos = getNodeBlockPos(pos);
591 block = getBlockNoCreate(blockpos);
593 catch(InvalidPositionException &e)
598 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
599 MapNode n = block->getNode(relpos);
601 if(n.sunlight_propagates())
603 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
604 block->setNode(relpos, n);
606 modified_blocks.insert(blockpos, block);
610 // Turn mud into grass
611 if(n.d == CONTENT_MUD)
614 block->setNode(relpos, n);
615 modified_blocks.insert(blockpos, block);
618 // Sunlight goes no further
625 void Map::updateLighting(enum LightBank bank,
626 core::map<v3s16, MapBlock*> & a_blocks,
627 core::map<v3s16, MapBlock*> & modified_blocks)
629 /*m_dout<<DTIME<<"Map::updateLighting(): "
630 <<a_blocks.size()<<" blocks."<<std::endl;*/
632 //TimeTaker timer("updateLighting");
636 //u32 count_was = modified_blocks.size();
638 core::map<v3s16, MapBlock*> blocks_to_update;
640 core::map<v3s16, bool> light_sources;
642 core::map<v3s16, u8> unlight_from;
644 core::map<v3s16, MapBlock*>::Iterator i;
645 i = a_blocks.getIterator();
646 for(; i.atEnd() == false; i++)
648 MapBlock *block = i.getNode()->getValue();
652 // Don't bother with dummy blocks.
656 v3s16 pos = block->getPos();
657 modified_blocks.insert(pos, block);
659 blocks_to_update.insert(pos, block);
662 Clear all light from block
664 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
665 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
666 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
671 MapNode n = block->getNode(v3s16(x,y,z));
672 u8 oldlight = n.getLight(bank);
674 block->setNode(v3s16(x,y,z), n);
676 // Collect borders for unlighting
677 if(x==0 || x == MAP_BLOCKSIZE-1
678 || y==0 || y == MAP_BLOCKSIZE-1
679 || z==0 || z == MAP_BLOCKSIZE-1)
681 v3s16 p_map = p + v3s16(
684 MAP_BLOCKSIZE*pos.Z);
685 unlight_from.insert(p_map, oldlight);
688 catch(InvalidPositionException &e)
691 This would happen when dealing with a
695 dstream<<"updateLighting(): InvalidPositionException"
700 if(bank == LIGHTBANK_DAY)
702 bool bottom_valid = block->propagateSunlight(light_sources);
704 // If bottom is valid, we're done.
708 else if(bank == LIGHTBANK_NIGHT)
710 // For night lighting, sunlight is not propagated
715 // Invalid lighting bank
719 /*dstream<<"Bottom for sunlight-propagated block ("
720 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
723 // Bottom sunlight is not valid; get the block and loop to it
727 block = getBlockNoCreate(pos);
729 catch(InvalidPositionException &e)
739 TimeTaker timer("unspreadLight");
740 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
745 u32 diff = modified_blocks.size() - count_was;
746 count_was = modified_blocks.size();
747 dstream<<"unspreadLight modified "<<diff<<std::endl;
751 TimeTaker timer("spreadLight");
752 spreadLight(bank, light_sources, modified_blocks);
757 u32 diff = modified_blocks.size() - count_was;
758 count_was = modified_blocks.size();
759 dstream<<"spreadLight modified "<<diff<<std::endl;
764 //MapVoxelManipulator vmanip(this);
766 // Make a manual voxel manipulator and load all the blocks
767 // that touch the requested blocks
768 ManualMapVoxelManipulator vmanip(this);
769 core::map<v3s16, MapBlock*>::Iterator i;
770 i = blocks_to_update.getIterator();
771 for(; i.atEnd() == false; i++)
773 MapBlock *block = i.getNode()->getValue();
774 v3s16 p = block->getPos();
776 // Add all surrounding blocks
777 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
780 Add all surrounding blocks that have up-to-date lighting
781 NOTE: This doesn't quite do the job (not everything
782 appropriate is lighted)
784 /*for(s16 z=-1; z<=1; z++)
785 for(s16 y=-1; y<=1; y++)
786 for(s16 x=-1; x<=1; x++)
789 MapBlock *block = getBlockNoCreateNoEx(p);
794 if(block->getLightingExpired())
796 vmanip.initialEmerge(p, p);
799 // Lighting of block will be updated completely
800 block->setLightingExpired(false);
804 //TimeTaker timer("unSpreadLight");
805 vmanip.unspreadLight(bank, unlight_from, light_sources);
808 //TimeTaker timer("spreadLight");
809 vmanip.spreadLight(bank, light_sources);
812 //TimeTaker timer("blitBack");
813 vmanip.blitBack(modified_blocks);
815 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
819 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
822 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
823 core::map<v3s16, MapBlock*> & modified_blocks)
825 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
826 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
829 Update information about whether day and night light differ
831 for(core::map<v3s16, MapBlock*>::Iterator
832 i = modified_blocks.getIterator();
833 i.atEnd() == false; i++)
835 MapBlock *block = i.getNode()->getValue();
836 block->updateDayNightDiff();
841 This is called after changing a node from transparent to opaque.
842 The lighting value of the node should be left as-is after changing
843 other values. This sets the lighting value to 0.
845 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
846 core::map<v3s16, MapBlock*> &modified_blocks)
849 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
850 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
853 From this node to nodes underneath:
854 If lighting is sunlight (1.0), unlight neighbours and
859 v3s16 toppos = p + v3s16(0,1,0);
860 v3s16 bottompos = p + v3s16(0,-1,0);
862 bool node_under_sunlight = true;
863 core::map<v3s16, bool> light_sources;
866 If there is a node at top and it doesn't have sunlight,
867 there has not been any sunlight going down.
869 Otherwise there probably is.
872 MapNode topnode = getNode(toppos);
874 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
875 node_under_sunlight = false;
877 catch(InvalidPositionException &e)
882 If the new node doesn't propagate sunlight and there is
883 grass below, change it to mud
885 if(content_features(n.d).sunlight_propagates == false)
888 MapNode bottomnode = getNode(bottompos);
890 if(bottomnode.d == CONTENT_GRASS
891 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
893 bottomnode.d = CONTENT_MUD;
894 setNode(bottompos, bottomnode);
897 catch(InvalidPositionException &e)
903 If the new node is mud and it is under sunlight, change it
906 if(n.d == CONTENT_MUD && node_under_sunlight)
912 Remove all light that has come out of this node
915 enum LightBank banks[] =
920 for(s32 i=0; i<2; i++)
922 enum LightBank bank = banks[i];
924 u8 lightwas = getNode(p).getLight(bank);
926 // Add the block of the added node to modified_blocks
927 v3s16 blockpos = getNodeBlockPos(p);
928 MapBlock * block = getBlockNoCreate(blockpos);
929 assert(block != NULL);
930 modified_blocks.insert(blockpos, block);
932 assert(isValidPosition(p));
934 // Unlight neighbours of node.
935 // This means setting light of all consequent dimmer nodes
937 // This also collects the nodes at the border which will spread
938 // light again into this.
939 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
945 If node lets sunlight through and is under sunlight, it has
948 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
950 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
954 Set the node on the map
963 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
966 NodeMetadata *meta = meta_proto->clone();
967 setNodeMetadata(p, meta);
971 If node is under sunlight and doesn't let sunlight through,
972 take all sunlighted nodes under it and clear light from them
973 and from where the light has been spread.
974 TODO: This could be optimized by mass-unlighting instead
977 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
981 //m_dout<<DTIME<<"y="<<y<<std::endl;
982 v3s16 n2pos(p.X, y, p.Z);
988 catch(InvalidPositionException &e)
993 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
995 unLightNeighbors(LIGHTBANK_DAY,
996 n2pos, n2.getLight(LIGHTBANK_DAY),
997 light_sources, modified_blocks);
998 n2.setLight(LIGHTBANK_DAY, 0);
1006 for(s32 i=0; i<2; i++)
1008 enum LightBank bank = banks[i];
1011 Spread light from all nodes that might be capable of doing so
1013 spreadLight(bank, light_sources, modified_blocks);
1017 Update information about whether day and night light differ
1019 for(core::map<v3s16, MapBlock*>::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 MapBlock *block = i.getNode()->getValue();
1024 block->updateDayNightDiff();
1028 Add neighboring liquid nodes and the node itself if it is
1029 liquid (=water node was added) to transform queue.
1032 v3s16(0,0,0), // self
1033 v3s16(0,0,1), // back
1034 v3s16(0,1,0), // top
1035 v3s16(1,0,0), // right
1036 v3s16(0,0,-1), // front
1037 v3s16(0,-1,0), // bottom
1038 v3s16(-1,0,0), // left
1040 for(u16 i=0; i<7; i++)
1045 v3s16 p2 = p + dirs[i];
1047 MapNode n2 = getNode(p2);
1048 if(content_liquid(n2.d))
1050 m_transforming_liquid.push_back(p2);
1053 }catch(InvalidPositionException &e)
1061 void Map::removeNodeAndUpdate(v3s16 p,
1062 core::map<v3s16, MapBlock*> &modified_blocks)
1064 /*PrintInfo(m_dout);
1065 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1066 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1068 bool node_under_sunlight = true;
1070 v3s16 toppos = p + v3s16(0,1,0);
1072 // Node will be replaced with this
1073 u8 replace_material = CONTENT_AIR;
1076 If there is a node at top and it doesn't have sunlight,
1077 there will be no sunlight going down.
1080 MapNode topnode = getNode(toppos);
1082 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1083 node_under_sunlight = false;
1085 catch(InvalidPositionException &e)
1089 core::map<v3s16, bool> light_sources;
1091 enum LightBank banks[] =
1096 for(s32 i=0; i<2; i++)
1098 enum LightBank bank = banks[i];
1101 Unlight neighbors (in case the node is a light source)
1103 unLightNeighbors(bank, p,
1104 getNode(p).getLight(bank),
1105 light_sources, modified_blocks);
1109 Remove node metadata
1112 removeNodeMetadata(p);
1116 This also clears the lighting.
1120 n.d = replace_material;
1123 for(s32 i=0; i<2; i++)
1125 enum LightBank bank = banks[i];
1128 Recalculate lighting
1130 spreadLight(bank, light_sources, modified_blocks);
1133 // Add the block of the removed node to modified_blocks
1134 v3s16 blockpos = getNodeBlockPos(p);
1135 MapBlock * block = getBlockNoCreate(blockpos);
1136 assert(block != NULL);
1137 modified_blocks.insert(blockpos, block);
1140 If the removed node was under sunlight, propagate the
1141 sunlight down from it and then light all neighbors
1142 of the propagated blocks.
1144 if(node_under_sunlight)
1146 s16 ybottom = propagateSunlight(p, modified_blocks);
1147 /*m_dout<<DTIME<<"Node was under sunlight. "
1148 "Propagating sunlight";
1149 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1151 for(; y >= ybottom; y--)
1153 v3s16 p2(p.X, y, p.Z);
1154 /*m_dout<<DTIME<<"lighting neighbors of node ("
1155 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1157 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1162 // Set the lighting of this node to 0
1163 // TODO: Is this needed? Lighting is cleared up there already.
1165 MapNode n = getNode(p);
1166 n.setLight(LIGHTBANK_DAY, 0);
1169 catch(InvalidPositionException &e)
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1179 // Get the brightest neighbour node and propagate light from it
1180 v3s16 n2p = getBrightestNeighbour(bank, p);
1182 MapNode n2 = getNode(n2p);
1183 lightNeighbors(bank, n2p, modified_blocks);
1185 catch(InvalidPositionException &e)
1191 Update information about whether day and night light differ
1193 for(core::map<v3s16, MapBlock*>::Iterator
1194 i = modified_blocks.getIterator();
1195 i.atEnd() == false; i++)
1197 MapBlock *block = i.getNode()->getValue();
1198 block->updateDayNightDiff();
1202 Add neighboring liquid nodes to transform queue.
1205 v3s16(0,0,1), // back
1206 v3s16(0,1,0), // top
1207 v3s16(1,0,0), // right
1208 v3s16(0,0,-1), // front
1209 v3s16(0,-1,0), // bottom
1210 v3s16(-1,0,0), // left
1212 for(u16 i=0; i<6; i++)
1217 v3s16 p2 = p + dirs[i];
1219 MapNode n2 = getNode(p2);
1220 if(content_liquid(n2.d))
1222 m_transforming_liquid.push_back(p2);
1225 }catch(InvalidPositionException &e)
1231 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1234 event.type = MEET_ADDNODE;
1238 bool succeeded = true;
1240 core::map<v3s16, MapBlock*> modified_blocks;
1241 addNodeAndUpdate(p, n, modified_blocks);
1243 // Copy modified_blocks to event
1244 for(core::map<v3s16, MapBlock*>::Iterator
1245 i = modified_blocks.getIterator();
1246 i.atEnd()==false; i++)
1248 event.modified_blocks.insert(i.getNode()->getKey(), false);
1251 catch(InvalidPositionException &e){
1255 dispatchEvent(&event);
1260 bool Map::removeNodeWithEvent(v3s16 p)
1263 event.type = MEET_REMOVENODE;
1266 bool succeeded = true;
1268 core::map<v3s16, MapBlock*> modified_blocks;
1269 removeNodeAndUpdate(p, modified_blocks);
1271 // Copy modified_blocks to event
1272 for(core::map<v3s16, MapBlock*>::Iterator
1273 i = modified_blocks.getIterator();
1274 i.atEnd()==false; i++)
1276 event.modified_blocks.insert(i.getNode()->getKey(), false);
1279 catch(InvalidPositionException &e){
1283 dispatchEvent(&event);
1288 bool Map::dayNightDiffed(v3s16 blockpos)
1291 v3s16 p = blockpos + v3s16(0,0,0);
1292 MapBlock *b = getBlockNoCreate(p);
1293 if(b->dayNightDiffed())
1296 catch(InvalidPositionException &e){}
1299 v3s16 p = blockpos + v3s16(-1,0,0);
1300 MapBlock *b = getBlockNoCreate(p);
1301 if(b->dayNightDiffed())
1304 catch(InvalidPositionException &e){}
1306 v3s16 p = blockpos + v3s16(0,-1,0);
1307 MapBlock *b = getBlockNoCreate(p);
1308 if(b->dayNightDiffed())
1311 catch(InvalidPositionException &e){}
1313 v3s16 p = blockpos + v3s16(0,0,-1);
1314 MapBlock *b = getBlockNoCreate(p);
1315 if(b->dayNightDiffed())
1318 catch(InvalidPositionException &e){}
1321 v3s16 p = blockpos + v3s16(1,0,0);
1322 MapBlock *b = getBlockNoCreate(p);
1323 if(b->dayNightDiffed())
1326 catch(InvalidPositionException &e){}
1328 v3s16 p = blockpos + v3s16(0,1,0);
1329 MapBlock *b = getBlockNoCreate(p);
1330 if(b->dayNightDiffed())
1333 catch(InvalidPositionException &e){}
1335 v3s16 p = blockpos + v3s16(0,0,1);
1336 MapBlock *b = getBlockNoCreate(p);
1337 if(b->dayNightDiffed())
1340 catch(InvalidPositionException &e){}
1346 Updates usage timers
1348 void Map::timerUpdate(float dtime)
1350 JMutexAutoLock lock(m_sector_mutex);
1352 core::map<v2s16, MapSector*>::Iterator si;
1354 si = m_sectors.getIterator();
1355 for(; si.atEnd() == false; si++)
1357 MapSector *sector = si.getNode()->getValue();
1358 sector->usage_timer += dtime;
1362 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1365 Wait for caches to be removed before continuing.
1367 This disables the existence of caches while locked
1369 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1371 core::list<v2s16>::Iterator j;
1372 for(j=list.begin(); j!=list.end(); j++)
1374 MapSector *sector = m_sectors[*j];
1377 sector->deleteBlocks();
1382 If sector is in sector cache, remove it from there
1384 if(m_sector_cache == sector)
1386 m_sector_cache = NULL;
1389 Remove from map and delete
1391 m_sectors.remove(*j);
1397 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1398 core::list<v3s16> *deleted_blocks)
1400 JMutexAutoLock lock(m_sector_mutex);
1402 core::list<v2s16> sector_deletion_queue;
1403 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1404 for(; i.atEnd() == false; i++)
1406 MapSector *sector = i.getNode()->getValue();
1408 Delete sector from memory if it hasn't been used in a long time
1410 if(sector->usage_timer > timeout)
1412 sector_deletion_queue.push_back(i.getNode()->getKey());
1414 if(deleted_blocks != NULL)
1416 // Collect positions of blocks of sector
1417 MapSector *sector = i.getNode()->getValue();
1418 core::list<MapBlock*> blocks;
1419 sector->getBlocks(blocks);
1420 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1421 i != blocks.end(); i++)
1423 deleted_blocks->push_back((*i)->getPos());
1428 deleteSectors(sector_deletion_queue, only_blocks);
1429 return sector_deletion_queue.getSize();
1432 void Map::PrintInfo(std::ostream &out)
1437 #define WATER_DROP_BOOST 4
1439 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1441 DSTACK(__FUNCTION_NAME);
1442 //TimeTaker timer("transformLiquids()");
1445 u32 initial_size = m_transforming_liquid.size();
1447 /*if(initial_size != 0)
1448 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1450 while(m_transforming_liquid.size() != 0)
1453 Get a queued transforming liquid node
1455 v3s16 p0 = m_transforming_liquid.pop_front();
1457 MapNode n0 = getNode(p0);
1459 // Don't deal with non-liquids
1460 if(content_liquid(n0.d) == false)
1463 bool is_source = !content_flowing_liquid(n0.d);
1465 u8 liquid_level = 8;
1466 if(is_source == false)
1467 liquid_level = n0.param2 & 0x0f;
1469 // Turn possible source into non-source
1470 u8 nonsource_c = make_liquid_flowing(n0.d);
1473 If not source, check that some node flows into this one
1474 and what is the level of liquid in this one
1476 if(is_source == false)
1478 s8 new_liquid_level_max = -1;
1480 v3s16 dirs_from[5] = {
1481 v3s16(0,1,0), // top
1482 v3s16(0,0,1), // back
1483 v3s16(1,0,0), // right
1484 v3s16(0,0,-1), // front
1485 v3s16(-1,0,0), // left
1487 for(u16 i=0; i<5; i++)
1492 bool from_top = (i==0);
1494 v3s16 p2 = p0 + dirs_from[i];
1495 MapNode n2 = getNode(p2);
1497 if(content_liquid(n2.d))
1499 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1500 // Check that the liquids are the same type
1501 if(n2_nonsource_c != nonsource_c)
1503 dstream<<"WARNING: Not handling: different liquids"
1504 " collide"<<std::endl;
1507 bool n2_is_source = !content_flowing_liquid(n2.d);
1508 s8 n2_liquid_level = 8;
1509 if(n2_is_source == false)
1510 n2_liquid_level = n2.param2 & 0x07;
1512 s8 new_liquid_level = -1;
1515 //new_liquid_level = 7;
1516 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1517 new_liquid_level = 7;
1519 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1521 else if(n2_liquid_level > 0)
1523 new_liquid_level = n2_liquid_level - 1;
1526 if(new_liquid_level > new_liquid_level_max)
1527 new_liquid_level_max = new_liquid_level;
1530 }catch(InvalidPositionException &e)
1536 If liquid level should be something else, update it and
1537 add all the neighboring water nodes to the transform queue.
1539 if(new_liquid_level_max != liquid_level)
1541 if(new_liquid_level_max == -1)
1543 // Remove water alltoghether
1550 n0.param2 = new_liquid_level_max;
1554 // Block has been modified
1556 v3s16 blockpos = getNodeBlockPos(p0);
1557 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1559 modified_blocks.insert(blockpos, block);
1563 Add neighboring non-source liquid nodes to transform queue.
1566 v3s16(0,0,1), // back
1567 v3s16(0,1,0), // top
1568 v3s16(1,0,0), // right
1569 v3s16(0,0,-1), // front
1570 v3s16(0,-1,0), // bottom
1571 v3s16(-1,0,0), // left
1573 for(u16 i=0; i<6; i++)
1578 v3s16 p2 = p0 + dirs[i];
1580 MapNode n2 = getNode(p2);
1581 if(content_flowing_liquid(n2.d))
1583 m_transforming_liquid.push_back(p2);
1586 }catch(InvalidPositionException &e)
1593 // Get a new one from queue if the node has turned into non-water
1594 if(content_liquid(n0.d) == false)
1598 Flow water from this node
1600 v3s16 dirs_to[5] = {
1601 v3s16(0,-1,0), // bottom
1602 v3s16(0,0,1), // back
1603 v3s16(1,0,0), // right
1604 v3s16(0,0,-1), // front
1605 v3s16(-1,0,0), // left
1607 for(u16 i=0; i<5; i++)
1612 bool to_bottom = (i == 0);
1614 // If liquid is at lowest possible height, it's not going
1615 // anywhere except down
1616 if(liquid_level == 0 && to_bottom == false)
1619 u8 liquid_next_level = 0;
1620 // If going to bottom
1623 //liquid_next_level = 7;
1624 if(liquid_level >= 7 - WATER_DROP_BOOST)
1625 liquid_next_level = 7;
1627 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1630 liquid_next_level = liquid_level - 1;
1632 bool n2_changed = false;
1633 bool flowed = false;
1635 v3s16 p2 = p0 + dirs_to[i];
1637 MapNode n2 = getNode(p2);
1638 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1640 if(content_liquid(n2.d))
1642 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1643 // Check that the liquids are the same type
1644 if(n2_nonsource_c != nonsource_c)
1646 dstream<<"WARNING: Not handling: different liquids"
1647 " collide"<<std::endl;
1650 bool n2_is_source = !content_flowing_liquid(n2.d);
1651 u8 n2_liquid_level = 8;
1652 if(n2_is_source == false)
1653 n2_liquid_level = n2.param2 & 0x07;
1662 // Just flow into the source, nothing changes.
1663 // n2_changed is not set because destination didn't change
1668 if(liquid_next_level > liquid_level)
1670 n2.param2 = liquid_next_level;
1678 else if(n2.d == CONTENT_AIR)
1681 n2.param2 = liquid_next_level;
1688 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1692 m_transforming_liquid.push_back(p2);
1694 v3s16 blockpos = getNodeBlockPos(p2);
1695 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1697 modified_blocks.insert(blockpos, block);
1700 // If n2_changed to bottom, don't flow anywhere else
1701 if(to_bottom && flowed && !is_source)
1704 }catch(InvalidPositionException &e)
1710 //if(loopcount >= 100000)
1711 if(loopcount >= initial_size * 1)
1714 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1717 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1719 v3s16 blockpos = getNodeBlockPos(p);
1720 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1721 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1724 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1728 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1732 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1734 v3s16 blockpos = getNodeBlockPos(p);
1735 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1736 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1739 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1743 block->m_node_metadata.set(p_rel, meta);
1746 void Map::removeNodeMetadata(v3s16 p)
1748 v3s16 blockpos = getNodeBlockPos(p);
1749 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1750 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1753 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1757 block->m_node_metadata.remove(p_rel);
1760 void Map::nodeMetadataStep(float dtime,
1761 core::map<v3s16, MapBlock*> &changed_blocks)
1765 Currently there is no way to ensure that all the necessary
1766 blocks are loaded when this is run. (They might get unloaded)
1767 NOTE: ^- Actually, that might not be so. In a quick test it
1768 reloaded a block with a furnace when I walked back to it from
1771 core::map<v2s16, MapSector*>::Iterator si;
1772 si = m_sectors.getIterator();
1773 for(; si.atEnd() == false; si++)
1775 MapSector *sector = si.getNode()->getValue();
1776 core::list< MapBlock * > sectorblocks;
1777 sector->getBlocks(sectorblocks);
1778 core::list< MapBlock * >::Iterator i;
1779 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1781 MapBlock *block = *i;
1782 bool changed = block->m_node_metadata.step(dtime);
1784 changed_blocks[block->getPos()] = block;
1793 ServerMap::ServerMap(std::string savedir):
1799 //m_chunksize = 16; // Too slow
1800 m_chunksize = 8; // Takes a few seconds
1804 m_seed = (((u64)(myrand()%0xffff)<<0)
1805 + ((u64)(myrand()%0xffff)<<16)
1806 + ((u64)(myrand()%0xffff)<<32)
1807 + ((u64)(myrand()%0xffff)<<48));
1810 Experimental and debug stuff
1817 Try to load map; if not found, create a new one.
1820 m_savedir = savedir;
1821 m_map_saving_enabled = false;
1825 // If directory exists, check contents and load if possible
1826 if(fs::PathExists(m_savedir))
1828 // If directory is empty, it is safe to save into it.
1829 if(fs::GetDirListing(m_savedir).size() == 0)
1831 dstream<<DTIME<<"Server: Empty save directory is valid."
1833 m_map_saving_enabled = true;
1838 // Load map metadata (seed, chunksize)
1841 // Load chunk metadata
1844 catch(FileNotGoodException &e){
1845 dstream<<DTIME<<"WARNING: Server: Could not load "
1846 <<"metafile(s). Disabling chunk-based "
1847 <<"generation."<<std::endl;
1851 /*// Load sector (0,0) and throw and exception on fail
1852 if(loadSectorFull(v2s16(0,0)) == false)
1853 throw LoadError("Failed to load sector (0,0)");*/
1855 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1856 "metadata and sector (0,0) from "<<savedir<<
1857 ", assuming valid save directory."
1860 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1861 <<"and chunk metadata from "<<savedir
1862 <<", assuming valid save directory."
1865 m_map_saving_enabled = true;
1866 // Map loaded, not creating new one
1870 // If directory doesn't exist, it is safe to save to it
1872 m_map_saving_enabled = true;
1875 catch(std::exception &e)
1877 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1878 <<", exception: "<<e.what()<<std::endl;
1879 dstream<<"Please remove the map or fix it."<<std::endl;
1880 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1883 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1885 // Create zero sector
1886 emergeSector(v2s16(0,0));
1888 // Initially write whole map
1892 ServerMap::~ServerMap()
1896 if(m_map_saving_enabled)
1899 // Save only changed parts
1901 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1905 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1908 catch(std::exception &e)
1910 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1911 <<", exception: "<<e.what()<<std::endl;
1917 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1918 for(; i.atEnd() == false; i++)
1920 MapChunk *chunk = i.getNode()->getValue();
1926 Some helper functions for the map generator
1929 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1931 v3s16 em = vmanip.m_area.getExtent();
1932 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1933 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1934 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1936 for(y=y_nodes_max; y>=y_nodes_min; y--)
1938 MapNode &n = vmanip.m_data[i];
1939 if(content_walkable(n.d))
1942 vmanip.m_area.add_y(em, i, -1);
1944 if(y >= y_nodes_min)
1950 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1952 v3s16 em = vmanip.m_area.getExtent();
1953 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1954 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1955 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1957 for(y=y_nodes_max; y>=y_nodes_min; y--)
1959 MapNode &n = vmanip.m_data[i];
1960 if(content_walkable(n.d)
1961 && n.d != CONTENT_TREE
1962 && n.d != CONTENT_LEAVES)
1965 vmanip.m_area.add_y(em, i, -1);
1967 if(y >= y_nodes_min)
1973 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1975 MapNode treenode(CONTENT_TREE);
1976 MapNode leavesnode(CONTENT_LEAVES);
1978 s16 trunk_h = myrand_range(3, 6);
1980 for(s16 ii=0; ii<trunk_h; ii++)
1982 if(vmanip.m_area.contains(p1))
1983 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1987 // p1 is now the last piece of the trunk
1990 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1991 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1992 Buffer<u8> leaves_d(leaves_a.getVolume());
1993 for(s32 i=0; i<leaves_a.getVolume(); i++)
1996 // Force leaves at near the end of the trunk
1999 for(s16 z=-d; z<=d; z++)
2000 for(s16 y=-d; y<=d; y++)
2001 for(s16 x=-d; x<=d; x++)
2003 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2007 // Add leaves randomly
2008 for(u32 iii=0; iii<7; iii++)
2013 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2014 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2015 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2018 for(s16 z=0; z<=d; z++)
2019 for(s16 y=0; y<=d; y++)
2020 for(s16 x=0; x<=d; x++)
2022 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2026 // Blit leaves to vmanip
2027 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2028 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2029 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2033 if(vmanip.m_area.contains(p) == false)
2035 u32 vi = vmanip.m_area.index(p);
2036 if(vmanip.m_data[vi].d != CONTENT_AIR)
2038 u32 i = leaves_a.index(x,y,z);
2039 if(leaves_d[i] == 1)
2040 vmanip.m_data[vi] = leavesnode;
2045 Noise functions. Make sure seed is mangled differently in each one.
2048 // Amount of trees per area in nodes
2049 double tree_amount_2d(u64 seed, v2s16 p)
2051 double noise = noise2d_perlin(
2052 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2054 double zeroval = -0.3;
2058 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2061 #define AVERAGE_MUD_AMOUNT 4
2063 double base_rock_level_2d(u64 seed, v2s16 p)
2065 // The base ground level
2066 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2067 + 25. * noise2d_perlin(
2068 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2069 (seed>>32)+654879876, 6, 0.6);
2071 /*// A bit hillier one
2072 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2073 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2074 (seed>>27)+90340, 6, 0.69);
2078 // Higher ground level
2079 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2080 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2081 seed+85039, 5, 0.69);
2082 //higher = 30; // For debugging
2084 // Limit higher to at least base
2088 // Steepness factor of cliffs
2089 double b = 1.0 + 1.0 * noise2d_perlin(
2090 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2092 b = rangelim(b, 0.0, 1000.0);
2095 b = rangelim(b, 3.0, 1000.0);
2096 //dstream<<"b="<<b<<std::endl;
2099 // Offset to more low
2100 double a_off = -0.2;
2101 // High/low selector
2102 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2103 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2104 seed-359, 6, 0.7));*/
2105 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2106 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2107 seed-359, 5, 0.60));
2109 a = rangelim(a, 0.0, 1.0);
2111 //dstream<<"a="<<a<<std::endl;
2113 double h = base*(1.0-a) + higher*a;
2121 Adds random objects to block, depending on the content of the block
2123 void addRandomObjects(MapBlock *block)
2125 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2126 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2128 bool last_node_walkable = false;
2129 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2132 MapNode n = block->getNodeNoEx(p);
2133 if(n.d == CONTENT_IGNORE)
2135 if(content_features(n.d).liquid_type != LIQUID_NONE)
2137 if(content_features(n.d).walkable)
2139 last_node_walkable = true;
2142 if(last_node_walkable)
2144 // If block contains light information
2145 if(content_features(n.d).param_type == CPT_LIGHT)
2147 if(n.getLight(LIGHTBANK_DAY) <= 3)
2149 if(myrand() % 300 == 0)
2151 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2153 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2154 std::string data = obj->getStaticData();
2155 StaticObject s_obj(obj->getType(),
2156 obj->getBasePosition(), data);
2158 block->m_static_objects.insert(0, s_obj);
2159 block->m_static_objects.insert(0, s_obj);
2160 block->m_static_objects.insert(0, s_obj);
2161 block->m_static_objects.insert(0, s_obj);
2162 block->m_static_objects.insert(0, s_obj);
2163 block->m_static_objects.insert(0, s_obj);
2169 last_node_walkable = false;
2172 block->setChangedFlag();
2175 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2178 This is the main map generation method
2181 void makeChunk(ChunkMakeData *data)
2186 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2187 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2188 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2189 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2191 *(u32)h_blocks*MAP_BLOCKSIZE;
2192 v3s16 bigarea_blocks_min(
2193 data->sectorpos_bigbase.X,
2195 data->sectorpos_bigbase.Y
2197 v3s16 bigarea_blocks_max(
2198 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2200 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2202 s16 lighting_min_d = 0-data->max_spread_amount;
2203 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2204 + data->max_spread_amount-1;
2207 data->vmanip.clearFlag(0xff);
2209 TimeTaker timer_generate("makeChunk() generate");
2211 // Maximum height of the stone surface and obstacles.
2212 // This is used to disable cave generation from going too high.
2213 s16 stone_surface_max_y = 0;
2216 Generate general ground level to full area
2221 //TimeTaker timer1("ground level");
2223 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2224 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2227 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2230 Skip of already generated
2233 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2234 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2238 // Ground height at this point
2239 float surface_y_f = 0.0;
2241 // Use perlin noise for ground height
2242 surface_y_f = base_rock_level_2d(data->seed, p2d);
2244 /*// Experimental stuff
2246 float a = highlands_level_2d(data->seed, p2d);
2251 // Convert to integer
2252 s16 surface_y = (s16)surface_y_f;
2255 if(surface_y > stone_surface_max_y)
2256 stone_surface_max_y = surface_y;
2259 Fill ground with stone
2262 // Use fast index incrementing
2263 v3s16 em = data->vmanip.m_area.getExtent();
2264 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2265 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2267 // Skip if already generated.
2268 // This is done here because there might be a cave at
2269 // any point in ground, which could look like it
2270 // wasn't generated.
2271 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2274 data->vmanip.m_data[i].d = CONTENT_STONE;
2276 data->vmanip.m_area.add_y(em, i, 1);
2284 Randomize some parameters
2287 //s32 stone_obstacle_count = 0;
2288 /*s32 stone_obstacle_count =
2289 rangelim((1.0+noise2d(data->seed+897,
2290 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2292 //s16 stone_obstacle_max_height = 0;
2293 /*s16 stone_obstacle_max_height =
2294 rangelim((1.0+noise2d(data->seed+5902,
2295 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2298 Loop this part, it will make stuff look older and newer nicely
2300 //for(u32 i_age=0; i_age<1; i_age++)
2301 for(u32 i_age=0; i_age<2; i_age++)
2303 /******************************
2304 BEGINNING OF AGING LOOP
2305 ******************************/
2309 //TimeTaker timer1("caves");
2314 u32 caves_count = relative_volume / 400000;
2315 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2316 if(stone_surface_max_y < WATER_LEVEL)
2318 /*u32 caves_count = 0;
2319 u32 bruises_count = 0;*/
2320 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2322 s16 min_tunnel_diameter = 3;
2323 s16 max_tunnel_diameter = 5;
2324 u16 tunnel_routepoints = 20;
2326 v3f main_direction(0,0,0);
2328 bool bruise_surface = (jj > caves_count);
2332 min_tunnel_diameter = 5;
2333 max_tunnel_diameter = myrand_range(10, 20);
2334 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2335 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2337 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2338 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2340 tunnel_routepoints = 5;
2346 // Allowed route area size in nodes
2348 data->sectorpos_base_size*MAP_BLOCKSIZE,
2349 h_blocks*MAP_BLOCKSIZE,
2350 data->sectorpos_base_size*MAP_BLOCKSIZE
2353 // Area starting point in nodes
2355 data->sectorpos_base.X*MAP_BLOCKSIZE,
2356 data->y_blocks_min*MAP_BLOCKSIZE,
2357 data->sectorpos_base.Y*MAP_BLOCKSIZE
2361 //(this should be more than the maximum radius of the tunnel)
2362 //s16 insure = 5; // Didn't work with max_d = 20
2364 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2365 ar += v3s16(1,0,1) * more * 2;
2366 of -= v3s16(1,0,1) * more;
2368 s16 route_y_min = 0;
2369 // Allow half a diameter + 7 over stone surface
2370 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2372 /*// If caves, don't go through surface too often
2373 if(bruise_surface == false)
2374 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2376 // Limit maximum to area
2377 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2381 /*// Minimum is at y=0
2382 route_y_min = -of.Y - 0;*/
2383 // Minimum is at y=max_tunnel_diameter/4
2384 //route_y_min = -of.Y + max_tunnel_diameter/4;
2385 //s16 min = -of.Y + max_tunnel_diameter/4;
2386 s16 min = -of.Y + 0;
2387 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2388 route_y_min = rangelim(route_y_min, 0, route_y_max);
2391 /*dstream<<"route_y_min = "<<route_y_min
2392 <<", route_y_max = "<<route_y_max<<std::endl;*/
2394 s16 route_start_y_min = route_y_min;
2395 s16 route_start_y_max = route_y_max;
2397 // Start every 2nd cave from surface
2398 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2400 if(coming_from_surface)
2402 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2405 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2406 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2408 // Randomize starting position
2410 (float)(myrand()%ar.X)+0.5,
2411 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2412 (float)(myrand()%ar.Z)+0.5
2415 MapNode airnode(CONTENT_AIR);
2418 Generate some tunnel starting from orp
2421 for(u16 j=0; j<tunnel_routepoints; j++)
2423 if(j%7==0 && bruise_surface == false)
2425 main_direction = v3f(
2426 ((float)(myrand()%20)-(float)10)/10,
2427 ((float)(myrand()%20)-(float)10)/30,
2428 ((float)(myrand()%20)-(float)10)/10
2430 main_direction *= (float)myrand_range(1, 3);
2434 s16 min_d = min_tunnel_diameter;
2435 s16 max_d = max_tunnel_diameter;
2436 s16 rs = myrand_range(min_d, max_d);
2441 maxlen = v3s16(rs*7,rs*7,rs*7);
2445 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2450 if(coming_from_surface && j < 3)
2453 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2454 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2455 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2461 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2462 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2463 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2467 vec += main_direction;
2472 else if(rp.X >= ar.X)
2474 if(rp.Y < route_y_min)
2476 else if(rp.Y >= route_y_max)
2477 rp.Y = route_y_max-1;
2480 else if(rp.Z >= ar.Z)
2484 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2486 v3f fp = orp + vec * f;
2487 v3s16 cp(fp.X, fp.Y, fp.Z);
2490 s16 d1 = d0 + rs - 1;
2491 for(s16 z0=d0; z0<=d1; z0++)
2493 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2494 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2495 for(s16 x0=-si; x0<=si-1; x0++)
2497 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2498 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2499 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2500 //s16 si2 = rs - abs(x0);
2501 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2507 /*if(isInArea(p, ar) == false)
2509 // Check only height
2510 if(y < 0 || y >= ar.Y)
2514 //assert(data->vmanip.m_area.contains(p));
2515 if(data->vmanip.m_area.contains(p) == false)
2517 dstream<<"WARNING: "<<__FUNCTION_NAME
2518 <<":"<<__LINE__<<": "
2519 <<"point not in area"
2524 // Just set it to air, it will be changed to
2526 u32 i = data->vmanip.m_area.index(p);
2527 data->vmanip.m_data[i] = airnode;
2529 if(bruise_surface == false)
2532 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2547 //TimeTaker timer1("ore veins");
2552 for(u32 jj=0; jj<relative_volume/1000; jj++)
2554 s16 max_vein_diameter = 3;
2556 // Allowed route area size in nodes
2558 data->sectorpos_base_size*MAP_BLOCKSIZE,
2559 h_blocks*MAP_BLOCKSIZE,
2560 data->sectorpos_base_size*MAP_BLOCKSIZE
2563 // Area starting point in nodes
2565 data->sectorpos_base.X*MAP_BLOCKSIZE,
2566 data->y_blocks_min*MAP_BLOCKSIZE,
2567 data->sectorpos_base.Y*MAP_BLOCKSIZE
2571 //(this should be more than the maximum radius of the tunnel)
2573 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2574 ar += v3s16(1,0,1) * more * 2;
2575 of -= v3s16(1,0,1) * more;
2577 // Randomize starting position
2579 (float)(myrand()%ar.X)+0.5,
2580 (float)(myrand()%ar.Y)+0.5,
2581 (float)(myrand()%ar.Z)+0.5
2584 // Randomize mineral
2587 mineral = MINERAL_COAL;
2589 mineral = MINERAL_IRON;
2592 Generate some vein starting from orp
2595 for(u16 j=0; j<2; j++)
2598 (float)(myrand()%ar.X)+0.5,
2599 (float)(myrand()%ar.Y)+0.5,
2600 (float)(myrand()%ar.Z)+0.5
2602 v3f vec = rp - orp;*/
2604 v3s16 maxlen(5, 5, 5);
2606 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2607 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2608 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2613 else if(rp.X >= ar.X)
2617 else if(rp.Y >= ar.Y)
2621 else if(rp.Z >= ar.Z)
2627 s16 max_d = max_vein_diameter;
2628 s16 rs = myrand_range(min_d, max_d);
2630 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2632 v3f fp = orp + vec * f;
2633 v3s16 cp(fp.X, fp.Y, fp.Z);
2635 s16 d1 = d0 + rs - 1;
2636 for(s16 z0=d0; z0<=d1; z0++)
2638 s16 si = rs - abs(z0);
2639 for(s16 x0=-si; x0<=si-1; x0++)
2641 s16 si2 = rs - abs(x0);
2642 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2644 // Don't put mineral to every place
2652 /*if(isInArea(p, ar) == false)
2654 // Check only height
2655 if(y < 0 || y >= ar.Y)
2659 assert(data->vmanip.m_area.contains(p));
2661 // Just set it to air, it will be changed to
2663 u32 i = data->vmanip.m_area.index(p);
2664 MapNode *n = &data->vmanip.m_data[i];
2665 if(n->d == CONTENT_STONE)
2680 //TimeTaker timer1("add mud");
2683 Add mud to the central chunk
2686 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2687 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2689 // Node position in 2d
2690 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2692 // Randomize mud amount
2693 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2694 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2695 data->seed+1, 3, 0.55));
2697 // Find ground level
2698 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2701 If topmost node is grass, change it to mud.
2702 It might be if it was flown to there from a neighboring
2703 chunk and then converted.
2706 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2707 MapNode *n = &data->vmanip.m_data[i];
2708 if(n->d == CONTENT_GRASS)
2717 v3s16 em = data->vmanip.m_area.getExtent();
2718 s16 y_start = surface_y+1;
2719 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2720 for(s16 y=y_start; y<=y_nodes_max; y++)
2722 if(mudcount >= mud_add_amount)
2725 MapNode &n = data->vmanip.m_data[i];
2729 data->vmanip.m_area.add_y(em, i, 1);
2738 TimeTaker timer1("flow mud");
2741 Flow mud away from steep edges
2744 // Limit area by 1 because mud is flown into neighbors.
2745 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2746 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2748 // Iterate a few times
2749 for(s16 k=0; k<3; k++)
2752 for(s16 x=mudflow_minpos;
2755 for(s16 z=mudflow_minpos;
2759 // Invert coordinates every 2nd iteration
2762 x = mudflow_maxpos - (x-mudflow_minpos);
2763 z = mudflow_maxpos - (z-mudflow_minpos);
2766 // Node position in 2d
2767 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2769 v3s16 em = data->vmanip.m_area.getExtent();
2770 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2777 for(; y>=y_nodes_min; y--)
2779 n = &data->vmanip.m_data[i];
2780 //if(content_walkable(n->d))
2782 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2785 data->vmanip.m_area.add_y(em, i, -1);
2788 // Stop if out of area
2789 //if(data->vmanip.m_area.contains(i) == false)
2793 /*// If not mud, do nothing to it
2794 MapNode *n = &data->vmanip.m_data[i];
2795 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2799 Don't flow it if the stuff under it is not mud
2803 data->vmanip.m_area.add_y(em, i2, -1);
2804 // Cancel if out of area
2805 if(data->vmanip.m_area.contains(i2) == false)
2807 MapNode *n2 = &data->vmanip.m_data[i2];
2808 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2812 // Make it exactly mud
2815 /*s16 recurse_count = 0;
2819 v3s16(0,0,1), // back
2820 v3s16(1,0,0), // right
2821 v3s16(0,0,-1), // front
2822 v3s16(-1,0,0), // left
2825 // Theck that upper is air or doesn't exist.
2826 // Cancel dropping if upper keeps it in place
2828 data->vmanip.m_area.add_y(em, i3, 1);
2829 if(data->vmanip.m_area.contains(i3) == true
2830 && content_walkable(data->vmanip.m_data[i3].d) == true)
2837 for(u32 di=0; di<4; di++)
2839 v3s16 dirp = dirs4[di];
2842 data->vmanip.m_area.add_p(em, i2, dirp);
2843 // Fail if out of area
2844 if(data->vmanip.m_area.contains(i2) == false)
2846 // Check that side is air
2847 MapNode *n2 = &data->vmanip.m_data[i2];
2848 if(content_walkable(n2->d))
2850 // Check that under side is air
2851 data->vmanip.m_area.add_y(em, i2, -1);
2852 if(data->vmanip.m_area.contains(i2) == false)
2854 n2 = &data->vmanip.m_data[i2];
2855 if(content_walkable(n2->d))
2857 /*// Check that under that is air (need a drop of 2)
2858 data->vmanip.m_area.add_y(em, i2, -1);
2859 if(data->vmanip.m_area.contains(i2) == false)
2861 n2 = &data->vmanip.m_data[i2];
2862 if(content_walkable(n2->d))
2864 // Loop further down until not air
2866 data->vmanip.m_area.add_y(em, i2, -1);
2867 // Fail if out of area
2868 if(data->vmanip.m_area.contains(i2) == false)
2870 n2 = &data->vmanip.m_data[i2];
2871 }while(content_walkable(n2->d) == false);
2872 // Loop one up so that we're in air
2873 data->vmanip.m_area.add_y(em, i2, 1);
2874 n2 = &data->vmanip.m_data[i2];
2876 // Move mud to new place
2878 // Set old place to be air
2879 *n = MapNode(CONTENT_AIR);
2892 //TimeTaker timer1("add water");
2895 Add water to the central chunk (and a bit more)
2898 for(s16 x=0-data->max_spread_amount;
2899 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2901 for(s16 z=0-data->max_spread_amount;
2902 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2905 // Node position in 2d
2906 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2908 // Find ground level
2909 //s16 surface_y = find_ground_level(data->vmanip, p2d);
2912 If ground level is over water level, skip.
2913 NOTE: This leaves caves near water without water,
2914 which looks especially crappy when the nearby water
2915 won't start flowing either for some reason
2917 /*if(surface_y > WATER_LEVEL)
2924 v3s16 em = data->vmanip.m_area.getExtent();
2925 u8 light = LIGHT_MAX;
2926 // Start at global water surface level
2927 s16 y_start = WATER_LEVEL;
2928 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2929 MapNode *n = &data->vmanip.m_data[i];
2931 for(s16 y=y_start; y>=y_nodes_min; y--)
2933 n = &data->vmanip.m_data[i];
2935 // Stop when there is no water and no air
2936 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2937 && n->d != CONTENT_WATER)
2943 // Make water only not in caves
2944 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
2946 n->d = CONTENT_WATERSOURCE;
2947 //n->setLight(LIGHTBANK_DAY, light);
2949 // Add to transforming liquid queue (in case it'd
2951 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2952 data->transforming_liquid.push_back(p);
2956 data->vmanip.m_area.add_y(em, i, -1);
2967 /***********************
2969 ************************/
2972 //TimeTaker timer1("convert mud to sand");
2978 //s16 mud_add_amount = myrand_range(2, 4);
2979 //s16 mud_add_amount = 0;
2981 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2982 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
2983 for(s16 x=0-data->max_spread_amount+1;
2984 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
2986 for(s16 z=0-data->max_spread_amount+1;
2987 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
2990 // Node position in 2d
2991 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2993 // Determine whether to have sand here
2994 double sandnoise = noise2d_perlin(
2995 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
2996 data->seed+59420, 3, 0.50);
2998 bool have_sand = (sandnoise > -0.15);
3000 if(have_sand == false)
3003 // Find ground level
3004 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3006 if(surface_y > WATER_LEVEL + 2)
3010 v3s16 em = data->vmanip.m_area.getExtent();
3011 s16 y_start = surface_y;
3012 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3013 u32 not_sand_counter = 0;
3014 for(s16 y=y_start; y>=y_nodes_min; y--)
3016 MapNode *n = &data->vmanip.m_data[i];
3017 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3019 n->d = CONTENT_SAND;
3024 if(not_sand_counter > 3)
3028 data->vmanip.m_area.add_y(em, i, -1);
3037 //TimeTaker timer1("generate trees");
3043 // Divide area into parts
3045 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3046 double area = sidelen * sidelen;
3047 for(s16 x0=0; x0<div; x0++)
3048 for(s16 z0=0; z0<div; z0++)
3050 // Center position of part of division
3052 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3053 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3055 // Minimum edge of part of division
3057 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3058 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3060 // Maximum edge of part of division
3062 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3063 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3066 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3067 // Put trees in random places on part of division
3068 for(u32 i=0; i<tree_count; i++)
3070 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3071 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3072 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3073 // Don't make a tree under water level
3076 // Don't make a tree so high that it doesn't fit
3077 if(y > y_nodes_max - 6)
3081 Trees grow only on mud and grass
3084 u32 i = data->vmanip.m_area.index(v3s16(p));
3085 MapNode *n = &data->vmanip.m_data[i];
3086 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3091 make_tree(data->vmanip, p);
3094 /*u32 tree_max = relative_area / 60;
3095 //u32 count = myrand_range(0, tree_max);
3096 for(u32 i=0; i<count; i++)
3098 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3099 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3100 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3101 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3102 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3103 // Don't make a tree under water level
3108 make_tree(data->vmanip, p);
3116 //TimeTaker timer1("grow grass");
3122 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3123 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3124 for(s16 x=0-data->max_spread_amount;
3125 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3127 for(s16 z=0-data->max_spread_amount;
3128 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3131 // Node position in 2d
3132 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3135 Find the lowest surface to which enough light ends up
3138 Basically just wait until not air and not leaves.
3142 v3s16 em = data->vmanip.m_area.getExtent();
3143 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3145 // Go to ground level
3146 for(y=y_nodes_max; y>=y_nodes_min; y--)
3148 MapNode &n = data->vmanip.m_data[i];
3149 if(n.d != CONTENT_AIR
3150 && n.d != CONTENT_LEAVES)
3152 data->vmanip.m_area.add_y(em, i, -1);
3154 if(y >= y_nodes_min)
3157 surface_y = y_nodes_min;
3160 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3161 MapNode *n = &data->vmanip.m_data[i];
3162 if(n->d == CONTENT_MUD)
3163 n->d = CONTENT_GRASS;
3169 Initial lighting (sunlight)
3172 core::map<v3s16, bool> light_sources;
3175 // 750ms @cs=8, can't optimize more
3176 TimeTaker timer1("initial lighting");
3180 Go through the edges and add all nodes that have light to light_sources
3184 for(s16 i=0; i<4; i++)
3186 for(s16 j=lighting_min_d;
3193 if(i == 0 || i == 1)
3195 x = (i==0) ? lighting_min_d : lighting_max_d;
3204 z = (i==0) ? lighting_min_d : lighting_max_d;
3211 // Node position in 2d
3212 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3215 v3s16 em = data->vmanip.m_area.getExtent();
3216 s16 y_start = y_nodes_max;
3217 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3218 for(s16 y=y_start; y>=y_nodes_min; y--)
3220 MapNode *n = &data->vmanip.m_data[i];
3221 if(n->getLight(LIGHTBANK_DAY) != 0)
3223 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3225 //NOTE: This is broken, at least the index has to
3234 Go through the edges and apply sunlight to them, not caring
3239 for(s16 i=0; i<4; i++)
3241 for(s16 j=lighting_min_d;
3248 if(i == 0 || i == 1)
3250 x = (i==0) ? lighting_min_d : lighting_max_d;
3259 z = (i==0) ? lighting_min_d : lighting_max_d;
3266 // Node position in 2d
3267 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3269 // Loop from top to down
3271 u8 light = LIGHT_SUN;
3272 v3s16 em = data->vmanip.m_area.getExtent();
3273 s16 y_start = y_nodes_max;
3274 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3275 for(s16 y=y_start; y>=y_nodes_min; y--)
3277 MapNode *n = &data->vmanip.m_data[i];
3278 if(light_propagates_content(n->d) == false)
3282 else if(light != LIGHT_SUN
3283 || sunlight_propagates_content(n->d) == false)
3289 n->setLight(LIGHTBANK_DAY, light);
3290 n->setLight(LIGHTBANK_NIGHT, 0);
3294 // Insert light source
3295 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3298 // Increment index by y
3299 data->vmanip.m_area.add_y(em, i, -1);
3305 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3306 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3307 /*for(s16 x=0-data->max_spread_amount+1;
3308 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3310 for(s16 z=0-data->max_spread_amount+1;
3311 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3315 This has to be 1 smaller than the actual area, because
3316 neighboring nodes are checked.
3318 for(s16 x=lighting_min_d+1;
3319 x<=lighting_max_d-1;
3321 for(s16 z=lighting_min_d+1;
3322 z<=lighting_max_d-1;
3325 // Node position in 2d
3326 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3329 Apply initial sunlight
3332 u8 light = LIGHT_SUN;
3333 bool add_to_sources = false;
3334 v3s16 em = data->vmanip.m_area.getExtent();
3335 s16 y_start = y_nodes_max;
3336 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3337 for(s16 y=y_start; y>=y_nodes_min; y--)
3339 MapNode *n = &data->vmanip.m_data[i];
3341 if(light_propagates_content(n->d) == false)
3345 else if(light != LIGHT_SUN
3346 || sunlight_propagates_content(n->d) == false)
3352 // This doesn't take much time
3353 if(add_to_sources == false)
3356 Check sides. If side is not air or water, start
3357 adding to light_sources.
3360 v3s16(0,0,1), // back
3361 v3s16(1,0,0), // right
3362 v3s16(0,0,-1), // front
3363 v3s16(-1,0,0), // left
3365 for(u32 di=0; di<4; di++)
3367 v3s16 dirp = dirs4[di];
3369 data->vmanip.m_area.add_p(em, i2, dirp);
3370 MapNode *n2 = &data->vmanip.m_data[i2];
3372 n2->d != CONTENT_AIR
3373 && n2->d != CONTENT_WATERSOURCE
3374 && n2->d != CONTENT_WATER
3376 add_to_sources = true;
3382 n->setLight(LIGHTBANK_DAY, light);
3383 n->setLight(LIGHTBANK_NIGHT, 0);
3385 // This doesn't take much time
3386 if(light != 0 && add_to_sources)
3388 // Insert light source
3389 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3392 // Increment index by y
3393 data->vmanip.m_area.add_y(em, i, -1);
3401 // Spread light around
3403 TimeTaker timer("makeChunk() spreadLight");
3404 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3411 timer_generate.stop();
3414 //###################################################################
3415 //###################################################################
3416 //###################################################################
3417 //###################################################################
3418 //###################################################################
3419 //###################################################################
3420 //###################################################################
3421 //###################################################################
3422 //###################################################################
3423 //###################################################################
3424 //###################################################################
3425 //###################################################################
3426 //###################################################################
3427 //###################################################################
3428 //###################################################################
3430 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3432 if(m_chunksize == 0)
3440 // The distance how far into the neighbors the generator is allowed to go.
3441 s16 max_spread_amount_sectors = 2;
3442 assert(max_spread_amount_sectors <= m_chunksize);
3443 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3445 s16 y_blocks_min = -4;
3446 s16 y_blocks_max = 3;
3448 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3449 s16 sectorpos_base_size = m_chunksize;
3451 v2s16 sectorpos_bigbase =
3452 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3453 s16 sectorpos_bigbase_size =
3454 sectorpos_base_size + 2 * max_spread_amount_sectors;
3457 data.chunkpos = chunkpos;
3458 data.y_blocks_min = y_blocks_min;
3459 data.y_blocks_max = y_blocks_max;
3460 data.sectorpos_base = sectorpos_base;
3461 data.sectorpos_base_size = sectorpos_base_size;
3462 data.sectorpos_bigbase = sectorpos_bigbase;
3463 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3464 data.max_spread_amount = max_spread_amount;
3467 Create the whole area of this and the neighboring chunks
3470 TimeTaker timer("initChunkMake() create area");
3472 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3473 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3475 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3476 ServerMapSector *sector = createSector(sectorpos);
3479 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3481 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3482 MapBlock *block = createBlock(blockpos);
3484 // Lighting won't be calculated
3485 //block->setLightingExpired(true);
3486 // Lighting will be calculated
3487 block->setLightingExpired(false);
3490 Block gets sunlight if this is true.
3492 This should be set to true when the top side of a block
3493 is completely exposed to the sky.
3495 Actually this doesn't matter now because the
3496 initial lighting is done here.
3498 block->setIsUnderground(y != y_blocks_max);
3504 Now we have a big empty area.
3506 Make a ManualMapVoxelManipulator that contains this and the
3510 v3s16 bigarea_blocks_min(
3511 sectorpos_bigbase.X,
3515 v3s16 bigarea_blocks_max(
3516 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3518 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3521 data.vmanip.setMap(this);
3524 TimeTaker timer("initChunkMake() initialEmerge");
3525 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3530 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3531 core::map<v3s16, MapBlock*> &changed_blocks)
3537 Blit generated stuff to map
3541 //TimeTaker timer("generateChunkRaw() blitBackAll");
3542 data.vmanip.blitBackAll(&changed_blocks);
3546 Update day/night difference cache of the MapBlocks
3549 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3550 i.atEnd() == false; i++)
3552 MapBlock *block = i.getNode()->getValue();
3553 block->updateDayNightDiff();
3558 Copy transforming liquid information
3560 while(data.transforming_liquid.size() > 0)
3562 v3s16 p = data.transforming_liquid.pop_front();
3563 m_transforming_liquid.push_back(p);
3567 Add random objects to blocks
3570 for(s16 x=0; x<data.sectorpos_base_size; x++)
3571 for(s16 z=0; z<data.sectorpos_base_size; z++)
3573 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3574 ServerMapSector *sector = createSector(sectorpos);
3577 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3579 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3580 MapBlock *block = createBlock(blockpos);
3581 addRandomObjects(block);
3587 Create chunk metadata
3590 for(s16 x=-1; x<=1; x++)
3591 for(s16 y=-1; y<=1; y++)
3593 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3594 // Add chunk meta information
3595 MapChunk *chunk = getChunk(chunkpos0);
3598 chunk = new MapChunk();
3599 m_chunks.insert(chunkpos0, chunk);
3601 //chunk->setIsVolatile(true);
3602 if(chunk->getGenLevel() > GENERATED_PARTLY)
3603 chunk->setGenLevel(GENERATED_PARTLY);
3607 Set central chunk non-volatile
3609 MapChunk *chunk = getChunk(data.chunkpos);
3612 //chunk->setIsVolatile(false);
3613 chunk->setGenLevel(GENERATED_FULLY);
3616 Save changed parts of map
3625 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3626 core::map<v3s16, MapBlock*> &changed_blocks,
3629 DSTACK(__FUNCTION_NAME);
3632 Don't generate if already fully generated
3636 MapChunk *chunk = getChunk(chunkpos);
3637 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3639 dstream<<"generateChunkRaw(): Chunk "
3640 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3641 <<" already generated"<<std::endl;
3646 dstream<<"generateChunkRaw(): Generating chunk "
3647 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3650 TimeTaker timer("generateChunkRaw()");
3654 // Initialize generation
3655 initChunkMake(data, chunkpos);
3660 // Finalize generation
3661 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3664 Return central chunk (which was requested)
3670 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3671 core::map<v3s16, MapBlock*> &changed_blocks)
3673 dstream<<"generateChunk(): Generating chunk "
3674 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3677 /*for(s16 x=-1; x<=1; x++)
3678 for(s16 y=-1; y<=1; y++)*/
3679 for(s16 x=-0; x<=0; x++)
3680 for(s16 y=-0; y<=0; y++)
3682 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3683 MapChunk *chunk = getChunk(chunkpos0);
3684 // Skip if already generated
3685 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3687 generateChunkRaw(chunkpos0, changed_blocks);
3690 assert(chunkNonVolatile(chunkpos1));
3692 MapChunk *chunk = getChunk(chunkpos1);
3697 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3699 DSTACK("%s: p2d=(%d,%d)",
3704 Check if it exists already in memory
3706 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3711 Try to load it from disk (with blocks)
3713 if(loadSectorFull(p2d) == true)
3715 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3718 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3719 throw InvalidPositionException("");
3725 Do not create over-limit
3727 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3728 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3729 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3730 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3731 throw InvalidPositionException("createSector(): pos. over limit");
3734 Generate blank sector
3737 sector = new ServerMapSector(this, p2d);
3739 // Sector position on map in nodes
3740 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3745 m_sectors.insert(p2d, sector);
3751 MapSector * ServerMap::emergeSector(v2s16 p2d,
3752 core::map<v3s16, MapBlock*> &changed_blocks)
3754 DSTACK("%s: p2d=(%d,%d)",
3761 v2s16 chunkpos = sector_to_chunk(p2d);
3762 /*bool chunk_nonvolatile = false;
3763 MapChunk *chunk = getChunk(chunkpos);
3764 if(chunk && chunk->getIsVolatile() == false)
3765 chunk_nonvolatile = true;*/
3766 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3769 If chunk is not fully generated, generate chunk
3771 if(chunk_nonvolatile == false)
3773 // Generate chunk and neighbors
3774 generateChunk(chunkpos, changed_blocks);
3778 Return sector if it exists now
3780 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3785 Try to load it from disk
3787 if(loadSectorFull(p2d) == true)
3789 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3792 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3793 throw InvalidPositionException("");
3799 generateChunk should have generated the sector
3803 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3804 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3808 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3810 return createSector(p2d);
3815 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3818 generateChunkRaw(chunkpos, changed_blocks, true);
3821 Return sector if it exists now
3823 sector = getSectorNoGenerateNoEx(p2d);
3827 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3835 //return generateSector();
3840 NOTE: This is not used for main map generation, only for blocks
3841 that are very high or low
3843 MapBlock * ServerMap::generateBlock(
3845 MapBlock *original_dummy,
3846 ServerMapSector *sector,
3847 core::map<v3s16, MapBlock*> &changed_blocks,
3848 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3851 DSTACK("%s: p=(%d,%d,%d)",
3855 // If chunks are disabled
3856 /*if(m_chunksize == 0)
3858 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
3859 <<"not generating."<<std::endl;
3863 /*dstream<<"generateBlock(): "
3864 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3867 MapBlock *block = original_dummy;
3869 v2s16 p2d(p.X, p.Z);
3871 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3874 Do not generate over-limit
3876 if(blockpos_over_limit(p))
3878 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3879 throw InvalidPositionException("generateBlock(): pos. over limit");
3883 If block doesn't exist, create one.
3884 If it exists, it is a dummy. In that case unDummify() it.
3886 NOTE: This already sets the map as the parent of the block
3890 block = sector->createBlankBlockNoInsert(block_y);
3894 // Remove the block so that nobody can get a half-generated one.
3895 sector->removeBlock(block);
3896 // Allocate the block to contain the generated data
3900 u8 water_material = CONTENT_WATERSOURCE;
3902 s32 lowest_ground_y = 32767;
3903 s32 highest_ground_y = -32768;
3905 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3906 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3908 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3910 //s16 surface_y = 0;
3912 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3913 + AVERAGE_MUD_AMOUNT;
3914 // If chunks are disabled
3915 if(m_chunksize == 0)
3916 surface_y = WATER_LEVEL + 1;
3918 if(surface_y < lowest_ground_y)
3919 lowest_ground_y = surface_y;
3920 if(surface_y > highest_ground_y)
3921 highest_ground_y = surface_y;
3923 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3925 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3927 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3932 NOTE: If there are some man-made structures above the
3933 newly created block, they won't be taken into account.
3935 if(real_y > surface_y)
3936 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3942 // If node is over heightmap y, it's air or water
3943 if(real_y > surface_y)
3945 // If under water level, it's water
3946 if(real_y < WATER_LEVEL)
3948 n.d = water_material;
3949 n.setLight(LIGHTBANK_DAY,
3950 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3952 Add to transforming liquid queue (in case it'd
3955 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3956 m_transforming_liquid.push_back(real_pos);
3962 // Else it's ground or caves (air)
3965 // If it's surface_depth under ground, it's stone
3966 if(real_y <= surface_y - surface_depth)
3968 n.d = CONTENT_STONE;
3972 // It is mud if it is under the first ground
3973 // level or under water
3974 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3980 n.d = CONTENT_GRASS;
3983 //n.d = CONTENT_MUD;
3985 /*// If under water level, it's mud
3986 if(real_y < WATER_LEVEL)
3988 // Only the topmost node is grass
3989 else if(real_y <= surface_y - 1)
3992 n.d = CONTENT_GRASS;*/
3996 block->setNode(v3s16(x0,y0,z0), n);
4001 Calculate some helper variables
4004 // Completely underground if the highest part of block is under lowest
4006 // This has to be very sure; it's probably one too strict now but
4007 // that's just better.
4008 bool completely_underground =
4009 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4011 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4013 bool mostly_underwater_surface = false;
4014 if(highest_ground_y < WATER_LEVEL
4015 && some_part_underground && !completely_underground)
4016 mostly_underwater_surface = true;
4019 Get local attributes
4022 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4024 float caves_amount = 0.5;
4029 NOTE: BEWARE: Too big amount of attribute points slows verything
4031 1 interpolation from 5000 points takes 2-3ms.
4033 //TimeTaker timer("generateBlock() local attribute retrieval");
4034 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4035 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4036 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4040 //dstream<<"generateBlock(): Done"<<std::endl;
4046 // Initialize temporary table
4047 const s32 ued = MAP_BLOCKSIZE;
4048 bool underground_emptiness[ued*ued*ued];
4049 for(s32 i=0; i<ued*ued*ued; i++)
4051 underground_emptiness[i] = 0;
4058 Initialize orp and ors. Try to find if some neighboring
4059 MapBlock has a tunnel ended in its side
4063 (float)(myrand()%ued)+0.5,
4064 (float)(myrand()%ued)+0.5,
4065 (float)(myrand()%ued)+0.5
4068 bool found_existing = false;
4074 for(s16 y=0; y<ued; y++)
4075 for(s16 x=0; x<ued; x++)
4077 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4078 if(getNode(ap).d == CONTENT_AIR)
4080 orp = v3f(x+1,y+1,0);
4081 found_existing = true;
4082 goto continue_generating;
4086 catch(InvalidPositionException &e){}
4092 for(s16 y=0; y<ued; y++)
4093 for(s16 x=0; x<ued; x++)
4095 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4096 if(getNode(ap).d == CONTENT_AIR)
4098 orp = v3f(x+1,y+1,ued-1);
4099 found_existing = true;
4100 goto continue_generating;
4104 catch(InvalidPositionException &e){}
4110 for(s16 y=0; y<ued; y++)
4111 for(s16 z=0; z<ued; z++)
4113 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4114 if(getNode(ap).d == CONTENT_AIR)
4116 orp = v3f(0,y+1,z+1);
4117 found_existing = true;
4118 goto continue_generating;
4122 catch(InvalidPositionException &e){}
4128 for(s16 y=0; y<ued; y++)
4129 for(s16 z=0; z<ued; z++)
4131 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4132 if(getNode(ap).d == CONTENT_AIR)
4134 orp = v3f(ued-1,y+1,z+1);
4135 found_existing = true;
4136 goto continue_generating;
4140 catch(InvalidPositionException &e){}
4146 for(s16 x=0; x<ued; x++)
4147 for(s16 z=0; z<ued; z++)
4149 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4150 if(getNode(ap).d == CONTENT_AIR)
4152 orp = v3f(x+1,0,z+1);
4153 found_existing = true;
4154 goto continue_generating;
4158 catch(InvalidPositionException &e){}
4164 for(s16 x=0; x<ued; x++)
4165 for(s16 z=0; z<ued; z++)
4167 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4168 if(getNode(ap).d == CONTENT_AIR)
4170 orp = v3f(x+1,ued-1,z+1);
4171 found_existing = true;
4172 goto continue_generating;
4176 catch(InvalidPositionException &e){}
4178 continue_generating:
4181 Choose whether to actually generate cave
4183 bool do_generate_caves = true;
4184 // Don't generate if no part is underground
4185 if(!some_part_underground)
4187 do_generate_caves = false;
4189 // Don't generate if mostly underwater surface
4190 /*else if(mostly_underwater_surface)
4192 do_generate_caves = false;
4194 // Partly underground = cave
4195 else if(!completely_underground)
4197 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4199 // Found existing cave underground
4200 else if(found_existing && completely_underground)
4202 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4204 // Underground and no caves found
4207 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4210 if(do_generate_caves)
4213 Generate some tunnel starting from orp and ors
4215 for(u16 i=0; i<3; i++)
4218 (float)(myrand()%ued)+0.5,
4219 (float)(myrand()%ued)+0.5,
4220 (float)(myrand()%ued)+0.5
4224 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4228 for(float f=0; f<1.0; f+=0.04)
4230 v3f fp = orp + vec * f;
4231 v3s16 cp(fp.X, fp.Y, fp.Z);
4233 s16 d1 = d0 + rs - 1;
4234 for(s16 z0=d0; z0<=d1; z0++)
4236 s16 si = rs - abs(z0);
4237 for(s16 x0=-si; x0<=si-1; x0++)
4239 s16 si2 = rs - abs(x0);
4240 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4246 if(isInArea(p, ued) == false)
4248 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4260 // Set to true if has caves.
4261 // Set when some non-air is changed to air when making caves.
4262 bool has_caves = false;
4265 Apply temporary cave data to block
4268 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4269 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4271 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4273 MapNode n = block->getNode(v3s16(x0,y0,z0));
4276 if(underground_emptiness[
4277 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4278 +ued*(y0*ued/MAP_BLOCKSIZE)
4279 +(x0*ued/MAP_BLOCKSIZE)])
4281 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4290 block->setNode(v3s16(x0,y0,z0), n);
4295 This is used for guessing whether or not the block should
4296 receive sunlight from the top if the block above doesn't exist
4298 block->setIsUnderground(completely_underground);
4301 Force lighting update if some part of block is partly
4302 underground and has caves.
4304 /*if(some_part_underground && !completely_underground && has_caves)
4306 //dstream<<"Half-ground caves"<<std::endl;
4307 lighting_invalidated_blocks[block->getPos()] = block;
4310 // DEBUG: Always update lighting
4311 //lighting_invalidated_blocks[block->getPos()] = block;
4317 if(some_part_underground)
4319 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4324 for(s16 i=0; i<underground_level/4 + 1; i++)
4326 if(myrand()%50 == 0)
4329 (myrand()%(MAP_BLOCKSIZE-2))+1,
4330 (myrand()%(MAP_BLOCKSIZE-2))+1,
4331 (myrand()%(MAP_BLOCKSIZE-2))+1
4337 for(u16 i=0; i<27; i++)
4339 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4341 block->setNode(cp+g_27dirs[i], n);
4349 u16 coal_amount = 30;
4350 u16 coal_rareness = 60 / coal_amount;
4351 if(coal_rareness == 0)
4353 if(myrand()%coal_rareness == 0)
4355 u16 a = myrand() % 16;
4356 u16 amount = coal_amount * a*a*a / 1000;
4357 for(s16 i=0; i<amount; i++)
4360 (myrand()%(MAP_BLOCKSIZE-2))+1,
4361 (myrand()%(MAP_BLOCKSIZE-2))+1,
4362 (myrand()%(MAP_BLOCKSIZE-2))+1
4366 n.d = CONTENT_STONE;
4367 n.param = MINERAL_COAL;
4369 for(u16 i=0; i<27; i++)
4371 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4373 block->setNode(cp+g_27dirs[i], n);
4381 //TODO: change to iron_amount or whatever
4382 u16 iron_amount = 15;
4383 u16 iron_rareness = 60 / iron_amount;
4384 if(iron_rareness == 0)
4386 if(myrand()%iron_rareness == 0)
4388 u16 a = myrand() % 16;
4389 u16 amount = iron_amount * a*a*a / 1000;
4390 for(s16 i=0; i<amount; i++)
4393 (myrand()%(MAP_BLOCKSIZE-2))+1,
4394 (myrand()%(MAP_BLOCKSIZE-2))+1,
4395 (myrand()%(MAP_BLOCKSIZE-2))+1
4399 n.d = CONTENT_STONE;
4400 n.param = MINERAL_IRON;
4402 for(u16 i=0; i<27; i++)
4404 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4406 block->setNode(cp+g_27dirs[i], n);
4413 Create a few rats in empty blocks underground
4415 if(completely_underground)
4417 //for(u16 i=0; i<2; i++)
4420 (myrand()%(MAP_BLOCKSIZE-2))+1,
4421 (myrand()%(MAP_BLOCKSIZE-2))+1,
4422 (myrand()%(MAP_BLOCKSIZE-2))+1
4425 // Check that the place is empty
4426 //if(!is_ground_content(block->getNode(cp).d))
4429 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4430 block->addObject(obj);
4436 Add block to sector.
4438 sector->insertBlock(block);
4440 // Lighting is invalid after generation.
4441 block->setLightingExpired(true);
4448 <<"lighting_invalidated_blocks.size()"
4452 <<" "<<lighting_invalidated_blocks.size()
4454 <<", "<<completely_underground
4455 <<", "<<some_part_underground
4462 MapBlock * ServerMap::createBlock(v3s16 p)
4464 DSTACK("%s: p=(%d,%d,%d)",
4465 __FUNCTION_NAME, p.X, p.Y, p.Z);
4468 Do not create over-limit
4470 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4471 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4472 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4473 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4474 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4475 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4476 throw InvalidPositionException("createBlock(): pos. over limit");
4478 v2s16 p2d(p.X, p.Z);
4481 This will create or load a sector if not found in memory.
4482 If block exists on disk, it will be loaded.
4484 NOTE: On old save formats, this will be slow, as it generates
4485 lighting on blocks for them.
4487 ServerMapSector *sector;
4489 sector = (ServerMapSector*)createSector(p2d);
4490 assert(sector->getId() == MAPSECTOR_SERVER);
4492 catch(InvalidPositionException &e)
4494 dstream<<"createBlock: createSector() failed"<<std::endl;
4498 NOTE: This should not be done, or at least the exception
4499 should not be passed on as std::exception, because it
4500 won't be catched at all.
4502 /*catch(std::exception &e)
4504 dstream<<"createBlock: createSector() failed: "
4505 <<e.what()<<std::endl;
4510 Try to get a block from the sector
4513 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4517 block = sector->createBlankBlock(block_y);
4521 MapBlock * ServerMap::emergeBlock(
4523 bool only_from_disk,
4524 core::map<v3s16, MapBlock*> &changed_blocks,
4525 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4528 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4530 p.X, p.Y, p.Z, only_from_disk);
4533 Do not generate over-limit
4535 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4536 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4537 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4538 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4539 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4540 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4541 throw InvalidPositionException("emergeBlock(): pos. over limit");
4543 v2s16 p2d(p.X, p.Z);
4546 This will create or load a sector if not found in memory.
4547 If block exists on disk, it will be loaded.
4549 ServerMapSector *sector;
4551 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4552 assert(sector->getId() == MAPSECTOR_SERVER);
4554 catch(InvalidPositionException &e)
4556 dstream<<"emergeBlock: emergeSector() failed: "
4557 <<e.what()<<std::endl;
4558 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4560 <<"You could try to delete it."<<std::endl;
4563 catch(VersionMismatchException &e)
4565 dstream<<"emergeBlock: emergeSector() failed: "
4566 <<e.what()<<std::endl;
4567 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4569 <<"You could try to delete it."<<std::endl;
4573 NOTE: This should not be done, or at least the exception
4574 should not be passed on as std::exception, because it
4575 won't be catched at all.
4577 /*catch(std::exception &e)
4579 dstream<<"emergeBlock: emergeSector() failed: "
4580 <<e.what()<<std::endl;
4581 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4583 <<"You could try to delete it."<<std::endl;
4588 Try to get a block from the sector
4591 bool does_not_exist = false;
4592 bool lighting_expired = false;
4593 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4597 does_not_exist = true;
4599 else if(block->isDummy() == true)
4601 does_not_exist = true;
4603 else if(block->getLightingExpired())
4605 lighting_expired = true;
4610 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4615 If block was not found on disk and not going to generate a
4616 new one, make sure there is a dummy block in place.
4618 if(only_from_disk && (does_not_exist || lighting_expired))
4620 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4624 // Create dummy block
4625 block = new MapBlock(this, p, true);
4627 // Add block to sector
4628 sector->insertBlock(block);
4634 //dstream<<"Not found on disk, generating."<<std::endl;
4636 //TimeTaker("emergeBlock() generate");
4638 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4641 If the block doesn't exist, generate the block.
4645 block = generateBlock(p, block, sector, changed_blocks,
4646 lighting_invalidated_blocks);
4649 if(lighting_expired)
4651 lighting_invalidated_blocks.insert(p, block);
4655 Initially update sunlight
4659 core::map<v3s16, bool> light_sources;
4660 bool black_air_left = false;
4661 bool bottom_invalid =
4662 block->propagateSunlight(light_sources, true,
4663 &black_air_left, true);
4665 // If sunlight didn't reach everywhere and part of block is
4666 // above ground, lighting has to be properly updated
4667 //if(black_air_left && some_part_underground)
4670 lighting_invalidated_blocks[block->getPos()] = block;
4675 lighting_invalidated_blocks[block->getPos()] = block;
4682 s16 ServerMap::findGroundLevel(v2s16 p2d)
4685 Uh, just do something random...
4687 // Find existing map from top to down
4690 v3s16 p(p2d.X, max, p2d.Y);
4691 for(; p.Y>min; p.Y--)
4693 MapNode n = getNodeNoEx(p);
4694 if(n.d != CONTENT_IGNORE)
4699 // If this node is not air, go to plan b
4700 if(getNodeNoEx(p).d != CONTENT_AIR)
4702 // Search existing walkable and return it
4703 for(; p.Y>min; p.Y--)
4705 MapNode n = getNodeNoEx(p);
4706 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4712 Plan B: Get from map generator perlin noise function
4714 // This won't work if proper generation is disabled
4715 if(m_chunksize == 0)
4716 return WATER_LEVEL+2;
4717 double level = base_rock_level_2d(m_seed, p2d);
4721 void ServerMap::createDir(std::string path)
4723 if(fs::CreateDir(path) == false)
4725 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4726 <<"\""<<path<<"\""<<std::endl;
4727 throw BaseException("ServerMap failed to create directory");
4731 std::string ServerMap::getSectorSubDir(v2s16 pos)
4734 snprintf(cc, 9, "%.4x%.4x",
4735 (unsigned int)pos.X&0xffff,
4736 (unsigned int)pos.Y&0xffff);
4738 return std::string(cc);
4741 std::string ServerMap::getSectorDir(v2s16 pos)
4743 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4746 v2s16 ServerMap::getSectorPos(std::string dirname)
4748 if(dirname.size() != 8)
4749 throw InvalidFilenameException("Invalid sector directory name");
4751 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4753 throw InvalidFilenameException("Invalid sector directory name");
4754 v2s16 pos((s16)x, (s16)y);
4758 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4760 v2s16 p2d = getSectorPos(sectordir);
4762 if(blockfile.size() != 4){
4763 throw InvalidFilenameException("Invalid block filename");
4766 int r = sscanf(blockfile.c_str(), "%4x", &y);
4768 throw InvalidFilenameException("Invalid block filename");
4769 return v3s16(p2d.X, y, p2d.Y);
4772 void ServerMap::save(bool only_changed)
4774 DSTACK(__FUNCTION_NAME);
4775 if(m_map_saving_enabled == false)
4777 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4781 if(only_changed == false)
4782 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4787 // Disable saving chunk metadata file if chunks are disabled
4788 if(m_chunksize != 0)
4793 u32 sector_meta_count = 0;
4794 u32 block_count = 0;
4797 JMutexAutoLock lock(m_sector_mutex);
4799 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4800 for(; i.atEnd() == false; i++)
4802 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4803 assert(sector->getId() == MAPSECTOR_SERVER);
4805 if(sector->differs_from_disk || only_changed == false)
4807 saveSectorMeta(sector);
4808 sector_meta_count++;
4810 core::list<MapBlock*> blocks;
4811 sector->getBlocks(blocks);
4812 core::list<MapBlock*>::Iterator j;
4813 for(j=blocks.begin(); j!=blocks.end(); j++)
4815 MapBlock *block = *j;
4816 if(block->getChangedFlag() || only_changed == false)
4821 /*dstream<<"ServerMap: Written block ("
4822 <<block->getPos().X<<","
4823 <<block->getPos().Y<<","
4824 <<block->getPos().Z<<")"
4833 Only print if something happened or saved whole map
4835 if(only_changed == false || sector_meta_count != 0
4836 || block_count != 0)
4838 dstream<<DTIME<<"ServerMap: Written: "
4839 <<sector_meta_count<<" sector metadata files, "
4840 <<block_count<<" block files"
4846 // NOTE: Doing this is insane. Deprecated and probably broken.
4847 void ServerMap::loadAll()
4849 DSTACK(__FUNCTION_NAME);
4850 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4855 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4857 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4859 JMutexAutoLock lock(m_sector_mutex);
4862 s32 printed_counter = -100000;
4863 s32 count = list.size();
4865 std::vector<fs::DirListNode>::iterator i;
4866 for(i=list.begin(); i!=list.end(); i++)
4868 if(counter > printed_counter + 10)
4870 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4871 printed_counter = counter;
4875 MapSector *sector = NULL;
4877 // We want directories
4881 sector = loadSectorMeta(i->name);
4883 catch(InvalidFilenameException &e)
4885 // This catches unknown crap in directory
4888 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4889 (m_savedir+"/sectors/"+i->name);
4890 std::vector<fs::DirListNode>::iterator i2;
4891 for(i2=list2.begin(); i2!=list2.end(); i2++)
4897 loadBlock(i->name, i2->name, sector);
4899 catch(InvalidFilenameException &e)
4901 // This catches unknown crap in directory
4905 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4910 void ServerMap::saveMasterHeightmap()
4912 DSTACK(__FUNCTION_NAME);
4914 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4916 createDir(m_savedir);
4918 /*std::string fullpath = m_savedir + "/master_heightmap";
4919 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4920 if(o.good() == false)
4921 throw FileNotGoodException("Cannot open master heightmap");*/
4923 // Format used for writing
4924 //u8 version = SER_FMT_VER_HIGHEST;
4927 void ServerMap::loadMasterHeightmap()
4929 DSTACK(__FUNCTION_NAME);
4931 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4933 /*std::string fullpath = m_savedir + "/master_heightmap";
4934 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4935 if(is.good() == false)
4936 throw FileNotGoodException("Cannot open master heightmap");*/
4940 void ServerMap::saveMapMeta()
4942 DSTACK(__FUNCTION_NAME);
4944 dstream<<"INFO: ServerMap::saveMapMeta(): "
4945 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4948 createDir(m_savedir);
4950 std::string fullpath = m_savedir + "/map_meta.txt";
4951 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4952 if(os.good() == false)
4954 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4955 <<"could not open"<<fullpath<<std::endl;
4956 throw FileNotGoodException("Cannot open chunk metadata");
4960 params.setU64("seed", m_seed);
4961 params.setS32("chunksize", m_chunksize);
4963 params.writeLines(os);
4965 os<<"[end_of_params]\n";
4969 void ServerMap::loadMapMeta()
4971 DSTACK(__FUNCTION_NAME);
4973 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
4976 std::string fullpath = m_savedir + "/map_meta.txt";
4977 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4978 if(is.good() == false)
4980 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4981 <<"could not open"<<fullpath<<std::endl;
4982 throw FileNotGoodException("Cannot open map metadata");
4990 throw SerializationError
4991 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4993 std::getline(is, line);
4994 std::string trimmedline = trim(line);
4995 if(trimmedline == "[end_of_params]")
4997 params.parseConfigLine(line);
5000 m_seed = params.getU64("seed");
5001 m_chunksize = params.getS32("chunksize");
5003 dstream<<"INFO: ServerMap::loadMapMeta(): "
5004 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5008 void ServerMap::saveChunkMeta()
5010 DSTACK(__FUNCTION_NAME);
5012 // This should not be called if chunks are disabled.
5013 assert(m_chunksize != 0);
5015 u32 count = m_chunks.size();
5017 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5018 <<count<<" chunks"<<std::endl;
5020 createDir(m_savedir);
5022 std::string fullpath = m_savedir + "/chunk_meta";
5023 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5024 if(os.good() == false)
5026 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5027 <<"could not open"<<fullpath<<std::endl;
5028 throw FileNotGoodException("Cannot open chunk metadata");
5034 os.write((char*)&version, 1);
5039 writeU32(buf, count);
5040 os.write((char*)buf, 4);
5042 for(core::map<v2s16, MapChunk*>::Iterator
5043 i = m_chunks.getIterator();
5044 i.atEnd()==false; i++)
5046 v2s16 p = i.getNode()->getKey();
5047 MapChunk *chunk = i.getNode()->getValue();
5050 os.write((char*)buf, 4);
5052 chunk->serialize(os, version);
5056 void ServerMap::loadChunkMeta()
5058 DSTACK(__FUNCTION_NAME);
5060 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5063 std::string fullpath = m_savedir + "/chunk_meta";
5064 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5065 if(is.good() == false)
5067 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5068 <<"could not open"<<fullpath<<std::endl;
5069 throw FileNotGoodException("Cannot open chunk metadata");
5075 is.read((char*)&version, 1);
5080 is.read((char*)buf, 4);
5081 u32 count = readU32(buf);
5083 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5084 <<count<<" chunks"<<std::endl;
5086 for(u32 i=0; i<count; i++)
5089 MapChunk *chunk = new MapChunk();
5091 is.read((char*)buf, 4);
5094 chunk->deSerialize(is, version);
5095 m_chunks.insert(p, chunk);
5099 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5101 DSTACK(__FUNCTION_NAME);
5102 // Format used for writing
5103 u8 version = SER_FMT_VER_HIGHEST;
5105 v2s16 pos = sector->getPos();
5106 createDir(m_savedir);
5107 createDir(m_savedir+"/sectors");
5108 std::string dir = getSectorDir(pos);
5111 std::string fullpath = dir + "/meta";
5112 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5113 if(o.good() == false)
5114 throw FileNotGoodException("Cannot open sector metafile");
5116 sector->serialize(o, version);
5118 sector->differs_from_disk = false;
5121 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5123 DSTACK(__FUNCTION_NAME);
5125 v2s16 p2d = getSectorPos(dirname);
5126 std::string dir = m_savedir + "/sectors/" + dirname;
5128 ServerMapSector *sector = NULL;
5130 std::string fullpath = dir + "/meta";
5131 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5132 if(is.good() == false)
5134 // If the directory exists anyway, it probably is in some old
5135 // format. Just go ahead and create the sector.
5136 if(fs::PathExists(dir))
5138 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5139 <<fullpath<<" doesn't exist but directory does."
5140 <<" Continuing with a sector with no metadata."
5142 sector = new ServerMapSector(this, p2d);
5143 m_sectors.insert(p2d, sector);
5146 throw FileNotGoodException("Cannot open sector metafile");
5150 sector = ServerMapSector::deSerialize
5151 (is, this, p2d, m_sectors);
5154 sector->differs_from_disk = false;
5159 bool ServerMap::loadSectorFull(v2s16 p2d)
5161 DSTACK(__FUNCTION_NAME);
5162 std::string sectorsubdir = getSectorSubDir(p2d);
5164 MapSector *sector = NULL;
5166 JMutexAutoLock lock(m_sector_mutex);
5169 sector = loadSectorMeta(sectorsubdir);
5171 catch(InvalidFilenameException &e)
5175 catch(FileNotGoodException &e)
5179 catch(std::exception &e)
5187 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5188 (m_savedir+"/sectors/"+sectorsubdir);
5189 std::vector<fs::DirListNode>::iterator i2;
5190 for(i2=list2.begin(); i2!=list2.end(); i2++)
5196 loadBlock(sectorsubdir, i2->name, sector);
5198 catch(InvalidFilenameException &e)
5200 // This catches unknown crap in directory
5206 void ServerMap::saveBlock(MapBlock *block)
5208 DSTACK(__FUNCTION_NAME);
5210 Dummy blocks are not written
5212 if(block->isDummy())
5214 /*v3s16 p = block->getPos();
5215 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5216 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5220 // Format used for writing
5221 u8 version = SER_FMT_VER_HIGHEST;
5223 v3s16 p3d = block->getPos();
5224 v2s16 p2d(p3d.X, p3d.Z);
5225 createDir(m_savedir);
5226 createDir(m_savedir+"/sectors");
5227 std::string dir = getSectorDir(p2d);
5230 // Block file is map/sectors/xxxxxxxx/xxxx
5232 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5233 std::string fullpath = dir + "/" + cc;
5234 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5235 if(o.good() == false)
5236 throw FileNotGoodException("Cannot open block data");
5239 [0] u8 serialization version
5242 o.write((char*)&version, 1);
5244 block->serialize(o, version);
5247 Versions up from 9 have block objects.
5251 block->serializeObjects(o, version);
5255 Versions up from 15 have static objects.
5259 block->m_static_objects.serialize(o);
5262 // We just wrote it to the disk
5263 block->resetChangedFlag();
5266 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5268 DSTACK(__FUNCTION_NAME);
5270 // Block file is map/sectors/xxxxxxxx/xxxx
5271 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5274 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5275 if(is.good() == false)
5276 throw FileNotGoodException("Cannot open block file");
5278 v3s16 p3d = getBlockPos(sectordir, blockfile);
5279 v2s16 p2d(p3d.X, p3d.Z);
5281 assert(sector->getPos() == p2d);
5283 u8 version = SER_FMT_VER_INVALID;
5284 is.read((char*)&version, 1);
5287 throw SerializationError("ServerMap::loadBlock(): Failed"
5288 " to read MapBlock version");
5290 /*u32 block_size = MapBlock::serializedLength(version);
5291 SharedBuffer<u8> data(block_size);
5292 is.read((char*)*data, block_size);*/
5294 // This will always return a sector because we're the server
5295 //MapSector *sector = emergeSector(p2d);
5297 MapBlock *block = NULL;
5298 bool created_new = false;
5300 block = sector->getBlockNoCreate(p3d.Y);
5302 catch(InvalidPositionException &e)
5304 block = sector->createBlankBlockNoInsert(p3d.Y);
5308 // deserialize block data
5309 block->deSerialize(is, version);
5312 Versions up from 9 have block objects.
5316 block->updateObjects(is, version, NULL, 0);
5320 Versions up from 15 have static objects.
5324 block->m_static_objects.deSerialize(is);
5328 sector->insertBlock(block);
5331 Convert old formats to new and save
5334 // Save old format blocks in new format
5335 if(version < SER_FMT_VER_HIGHEST)
5340 // We just loaded it from the disk, so it's up-to-date.
5341 block->resetChangedFlag();
5344 catch(SerializationError &e)
5346 dstream<<"WARNING: Invalid block data on disk "
5347 "(SerializationError). Ignoring. "
5348 "A new one will be generated."
5351 // TODO: Backup file; name is in fullpath.
5355 void ServerMap::PrintInfo(std::ostream &out)
5366 ClientMap::ClientMap(
5368 MapDrawControl &control,
5369 scene::ISceneNode* parent,
5370 scene::ISceneManager* mgr,
5374 scene::ISceneNode(parent, mgr, id),
5377 m_camera_position(0,0,0),
5378 m_camera_direction(0,0,1)
5380 m_camera_mutex.Init();
5381 assert(m_camera_mutex.IsInitialized());
5383 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5384 BS*1000000,BS*1000000,BS*1000000);
5387 ClientMap::~ClientMap()
5389 /*JMutexAutoLock lock(mesh_mutex);
5398 MapSector * ClientMap::emergeSector(v2s16 p2d)
5400 DSTACK(__FUNCTION_NAME);
5401 // Check that it doesn't exist already
5403 return getSectorNoGenerate(p2d);
5405 catch(InvalidPositionException &e)
5410 ClientMapSector *sector = new ClientMapSector(this, p2d);
5413 JMutexAutoLock lock(m_sector_mutex);
5414 m_sectors.insert(p2d, sector);
5420 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5422 DSTACK(__FUNCTION_NAME);
5423 ClientMapSector *sector = NULL;
5425 JMutexAutoLock lock(m_sector_mutex);
5427 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5431 sector = (ClientMapSector*)n->getValue();
5432 assert(sector->getId() == MAPSECTOR_CLIENT);
5436 sector = new ClientMapSector(this, p2d);
5438 JMutexAutoLock lock(m_sector_mutex);
5439 m_sectors.insert(p2d, sector);
5443 sector->deSerialize(is);
5446 void ClientMap::OnRegisterSceneNode()
5450 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5451 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5454 ISceneNode::OnRegisterSceneNode();
5457 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5459 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5460 DSTACK(__FUNCTION_NAME);
5462 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5465 Get time for measuring timeout.
5467 Measuring time is very useful for long delays when the
5468 machine is swapping a lot.
5470 int time1 = time(0);
5472 //u32 daynight_ratio = m_client->getDayNightRatio();
5474 m_camera_mutex.Lock();
5475 v3f camera_position = m_camera_position;
5476 v3f camera_direction = m_camera_direction;
5477 m_camera_mutex.Unlock();
5480 Get all blocks and draw all visible ones
5483 v3s16 cam_pos_nodes(
5484 camera_position.X / BS,
5485 camera_position.Y / BS,
5486 camera_position.Z / BS);
5488 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5490 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5491 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5493 // Take a fair amount as we will be dropping more out later
5495 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5496 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5497 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5499 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5500 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5501 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5503 u32 vertex_count = 0;
5505 // For limiting number of mesh updates per frame
5506 u32 mesh_update_count = 0;
5508 u32 blocks_would_have_drawn = 0;
5509 u32 blocks_drawn = 0;
5511 //NOTE: The sectors map should be locked but we're not doing it
5512 // because it'd cause too much delays
5514 int timecheck_counter = 0;
5515 core::map<v2s16, MapSector*>::Iterator si;
5516 si = m_sectors.getIterator();
5517 for(; si.atEnd() == false; si++)
5520 timecheck_counter++;
5521 if(timecheck_counter > 50)
5523 timecheck_counter = 0;
5524 int time2 = time(0);
5525 if(time2 > time1 + 4)
5527 dstream<<"ClientMap::renderMap(): "
5528 "Rendering takes ages, returning."
5535 MapSector *sector = si.getNode()->getValue();
5536 v2s16 sp = sector->getPos();
5538 if(m_control.range_all == false)
5540 if(sp.X < p_blocks_min.X
5541 || sp.X > p_blocks_max.X
5542 || sp.Y < p_blocks_min.Z
5543 || sp.Y > p_blocks_max.Z)
5547 core::list< MapBlock * > sectorblocks;
5548 sector->getBlocks(sectorblocks);
5554 core::list< MapBlock * >::Iterator i;
5555 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5557 MapBlock *block = *i;
5560 Compare block position to camera position, skip
5561 if not seen on display
5564 float range = 100000 * BS;
5565 if(m_control.range_all == false)
5566 range = m_control.wanted_range * BS;
5569 if(isBlockInSight(block->getPos(), camera_position,
5570 camera_direction, range, &d) == false)
5575 // This is ugly (spherical distance limit?)
5576 /*if(m_control.range_all == false &&
5577 d - 0.5*BS*MAP_BLOCKSIZE > range)
5582 Update expired mesh (used for day/night change)
5584 It doesn't work exactly like it should now with the
5585 tasked mesh update but whatever.
5588 bool mesh_expired = false;
5591 JMutexAutoLock lock(block->mesh_mutex);
5593 mesh_expired = block->getMeshExpired();
5595 // Mesh has not been expired and there is no mesh:
5596 // block has no content
5597 if(block->mesh == NULL && mesh_expired == false)
5601 f32 faraway = BS*50;
5602 //f32 faraway = m_control.wanted_range * BS;
5605 This has to be done with the mesh_mutex unlocked
5607 // Pretty random but this should work somewhat nicely
5608 if(mesh_expired && (
5609 (mesh_update_count < 3
5610 && (d < faraway || mesh_update_count < 2)
5613 (m_control.range_all && mesh_update_count < 20)
5616 /*if(mesh_expired && mesh_update_count < 6
5617 && (d < faraway || mesh_update_count < 3))*/
5619 mesh_update_count++;
5621 // Mesh has been expired: generate new mesh
5622 //block->updateMesh(daynight_ratio);
5623 m_client->addUpdateMeshTask(block->getPos());
5625 mesh_expired = false;
5630 Draw the faces of the block
5633 JMutexAutoLock lock(block->mesh_mutex);
5635 scene::SMesh *mesh = block->mesh;
5640 blocks_would_have_drawn++;
5641 if(blocks_drawn >= m_control.wanted_max_blocks
5642 && m_control.range_all == false
5643 && d > m_control.wanted_min_range * BS)
5647 u32 c = mesh->getMeshBufferCount();
5649 for(u32 i=0; i<c; i++)
5651 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5652 const video::SMaterial& material = buf->getMaterial();
5653 video::IMaterialRenderer* rnd =
5654 driver->getMaterialRenderer(material.MaterialType);
5655 bool transparent = (rnd && rnd->isTransparent());
5656 // Render transparent on transparent pass and likewise.
5657 if(transparent == is_transparent_pass)
5660 This *shouldn't* hurt too much because Irrlicht
5661 doesn't change opengl textures if the old
5662 material is set again.
5664 driver->setMaterial(buf->getMaterial());
5665 driver->drawMeshBuffer(buf);
5666 vertex_count += buf->getVertexCount();
5670 } // foreach sectorblocks
5673 m_control.blocks_drawn = blocks_drawn;
5674 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5676 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5677 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5680 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5681 core::map<v3s16, MapBlock*> *affected_blocks)
5683 bool changed = false;
5685 Add it to all blocks touching it
5688 v3s16(0,0,0), // this
5689 v3s16(0,0,1), // back
5690 v3s16(0,1,0), // top
5691 v3s16(1,0,0), // right
5692 v3s16(0,0,-1), // front
5693 v3s16(0,-1,0), // bottom
5694 v3s16(-1,0,0), // left
5696 for(u16 i=0; i<7; i++)
5698 v3s16 p2 = p + dirs[i];
5699 // Block position of neighbor (or requested) node
5700 v3s16 blockpos = getNodeBlockPos(p2);
5701 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5702 if(blockref == NULL)
5704 // Relative position of requested node
5705 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5706 if(blockref->setTempMod(relpos, mod))
5711 if(changed && affected_blocks!=NULL)
5713 for(u16 i=0; i<7; i++)
5715 v3s16 p2 = p + dirs[i];
5716 // Block position of neighbor (or requested) node
5717 v3s16 blockpos = getNodeBlockPos(p2);
5718 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5719 if(blockref == NULL)
5721 affected_blocks->insert(blockpos, blockref);
5727 bool ClientMap::clearTempMod(v3s16 p,
5728 core::map<v3s16, MapBlock*> *affected_blocks)
5730 bool changed = false;
5732 v3s16(0,0,0), // this
5733 v3s16(0,0,1), // back
5734 v3s16(0,1,0), // top
5735 v3s16(1,0,0), // right
5736 v3s16(0,0,-1), // front
5737 v3s16(0,-1,0), // bottom
5738 v3s16(-1,0,0), // left
5740 for(u16 i=0; i<7; i++)
5742 v3s16 p2 = p + dirs[i];
5743 // Block position of neighbor (or requested) node
5744 v3s16 blockpos = getNodeBlockPos(p2);
5745 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5746 if(blockref == NULL)
5748 // Relative position of requested node
5749 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5750 if(blockref->clearTempMod(relpos))
5755 if(changed && affected_blocks!=NULL)
5757 for(u16 i=0; i<7; i++)
5759 v3s16 p2 = p + dirs[i];
5760 // Block position of neighbor (or requested) node
5761 v3s16 blockpos = getNodeBlockPos(p2);
5762 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5763 if(blockref == NULL)
5765 affected_blocks->insert(blockpos, blockref);
5771 void ClientMap::expireMeshes(bool only_daynight_diffed)
5773 TimeTaker timer("expireMeshes()");
5775 core::map<v2s16, MapSector*>::Iterator si;
5776 si = m_sectors.getIterator();
5777 for(; si.atEnd() == false; si++)
5779 MapSector *sector = si.getNode()->getValue();
5781 core::list< MapBlock * > sectorblocks;
5782 sector->getBlocks(sectorblocks);
5784 core::list< MapBlock * >::Iterator i;
5785 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5787 MapBlock *block = *i;
5789 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5795 JMutexAutoLock lock(block->mesh_mutex);
5796 if(block->mesh != NULL)
5798 /*block->mesh->drop();
5799 block->mesh = NULL;*/
5800 block->setMeshExpired(true);
5807 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5809 assert(mapType() == MAPTYPE_CLIENT);
5812 v3s16 p = blockpos + v3s16(0,0,0);
5813 MapBlock *b = getBlockNoCreate(p);
5814 b->updateMesh(daynight_ratio);
5815 //b->setMeshExpired(true);
5817 catch(InvalidPositionException &e){}
5820 v3s16 p = blockpos + v3s16(-1,0,0);
5821 MapBlock *b = getBlockNoCreate(p);
5822 b->updateMesh(daynight_ratio);
5823 //b->setMeshExpired(true);
5825 catch(InvalidPositionException &e){}
5827 v3s16 p = blockpos + v3s16(0,-1,0);
5828 MapBlock *b = getBlockNoCreate(p);
5829 b->updateMesh(daynight_ratio);
5830 //b->setMeshExpired(true);
5832 catch(InvalidPositionException &e){}
5834 v3s16 p = blockpos + v3s16(0,0,-1);
5835 MapBlock *b = getBlockNoCreate(p);
5836 b->updateMesh(daynight_ratio);
5837 //b->setMeshExpired(true);
5839 catch(InvalidPositionException &e){}
5844 Update mesh of block in which the node is, and if the node is at the
5845 leading edge, update the appropriate leading blocks too.
5847 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5855 v3s16 blockposes[4];
5856 for(u32 i=0; i<4; i++)
5858 v3s16 np = nodepos + dirs[i];
5859 blockposes[i] = getNodeBlockPos(np);
5860 // Don't update mesh of block if it has been done already
5861 bool already_updated = false;
5862 for(u32 j=0; j<i; j++)
5864 if(blockposes[j] == blockposes[i])
5866 already_updated = true;
5873 MapBlock *b = getBlockNoCreate(blockposes[i]);
5874 b->updateMesh(daynight_ratio);
5879 void ClientMap::PrintInfo(std::ostream &out)
5890 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5895 MapVoxelManipulator::~MapVoxelManipulator()
5897 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5901 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5903 TimeTaker timer1("emerge", &emerge_time);
5905 // Units of these are MapBlocks
5906 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5907 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5909 VoxelArea block_area_nodes
5910 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5912 addArea(block_area_nodes);
5914 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5915 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5916 for(s32 x=p_min.X; x<=p_max.X; x++)
5919 core::map<v3s16, bool>::Node *n;
5920 n = m_loaded_blocks.find(p);
5924 bool block_data_inexistent = false;
5927 TimeTaker timer1("emerge load", &emerge_load_time);
5929 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5930 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5933 dstream<<std::endl;*/
5935 MapBlock *block = m_map->getBlockNoCreate(p);
5936 if(block->isDummy())
5937 block_data_inexistent = true;
5939 block->copyTo(*this);
5941 catch(InvalidPositionException &e)
5943 block_data_inexistent = true;
5946 if(block_data_inexistent)
5948 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5949 // Fill with VOXELFLAG_INEXISTENT
5950 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5951 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5953 s32 i = m_area.index(a.MinEdge.X,y,z);
5954 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5958 m_loaded_blocks.insert(p, !block_data_inexistent);
5961 //dstream<<"emerge done"<<std::endl;
5965 SUGG: Add an option to only update eg. water and air nodes.
5966 This will make it interfere less with important stuff if
5969 void MapVoxelManipulator::blitBack
5970 (core::map<v3s16, MapBlock*> & modified_blocks)
5972 if(m_area.getExtent() == v3s16(0,0,0))
5975 //TimeTaker timer1("blitBack");
5977 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5978 <<m_loaded_blocks.size()<<std::endl;*/
5981 Initialize block cache
5983 v3s16 blockpos_last;
5984 MapBlock *block = NULL;
5985 bool block_checked_in_modified = false;
5987 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5988 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5989 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5993 u8 f = m_flags[m_area.index(p)];
5994 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5997 MapNode &n = m_data[m_area.index(p)];
5999 v3s16 blockpos = getNodeBlockPos(p);
6004 if(block == NULL || blockpos != blockpos_last){
6005 block = m_map->getBlockNoCreate(blockpos);
6006 blockpos_last = blockpos;
6007 block_checked_in_modified = false;
6010 // Calculate relative position in block
6011 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6013 // Don't continue if nothing has changed here
6014 if(block->getNode(relpos) == n)
6017 //m_map->setNode(m_area.MinEdge + p, n);
6018 block->setNode(relpos, n);
6021 Make sure block is in modified_blocks
6023 if(block_checked_in_modified == false)
6025 modified_blocks[blockpos] = block;
6026 block_checked_in_modified = true;
6029 catch(InvalidPositionException &e)
6035 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6036 MapVoxelManipulator(map),
6037 m_create_area(false)
6041 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6045 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6047 // Just create the area so that it can be pointed to
6048 VoxelManipulator::emerge(a, caller_id);
6051 void ManualMapVoxelManipulator::initialEmerge(
6052 v3s16 blockpos_min, v3s16 blockpos_max)
6054 TimeTaker timer1("initialEmerge", &emerge_time);
6056 // Units of these are MapBlocks
6057 v3s16 p_min = blockpos_min;
6058 v3s16 p_max = blockpos_max;
6060 VoxelArea block_area_nodes
6061 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6063 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6066 dstream<<"initialEmerge: area: ";
6067 block_area_nodes.print(dstream);
6068 dstream<<" ("<<size_MB<<"MB)";
6072 addArea(block_area_nodes);
6074 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6075 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6076 for(s32 x=p_min.X; x<=p_max.X; x++)
6079 core::map<v3s16, bool>::Node *n;
6080 n = m_loaded_blocks.find(p);
6084 bool block_data_inexistent = false;
6087 TimeTaker timer1("emerge load", &emerge_load_time);
6089 MapBlock *block = m_map->getBlockNoCreate(p);
6090 if(block->isDummy())
6091 block_data_inexistent = true;
6093 block->copyTo(*this);
6095 catch(InvalidPositionException &e)
6097 block_data_inexistent = true;
6100 if(block_data_inexistent)
6103 Mark area inexistent
6105 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6106 // Fill with VOXELFLAG_INEXISTENT
6107 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6108 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6110 s32 i = m_area.index(a.MinEdge.X,y,z);
6111 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6115 m_loaded_blocks.insert(p, !block_data_inexistent);
6119 void ManualMapVoxelManipulator::blitBackAll(
6120 core::map<v3s16, MapBlock*> * modified_blocks)
6122 if(m_area.getExtent() == v3s16(0,0,0))
6126 Copy data of all blocks
6128 for(core::map<v3s16, bool>::Iterator
6129 i = m_loaded_blocks.getIterator();
6130 i.atEnd() == false; i++)
6132 bool existed = i.getNode()->getValue();
6133 if(existed == false)
6135 v3s16 p = i.getNode()->getKey();
6136 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6139 dstream<<"WARNING: "<<__FUNCTION_NAME
6140 <<": got NULL block "
6141 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6146 block->copyFrom(*this);
6149 modified_blocks->insert(p, block);