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); // Bulk comment-out
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); // Bulk comment-out
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); // Bulk comment-out
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);
2166 if(myrand() % 300 == 0)
2168 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2170 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2171 std::string data = obj->getStaticData();
2172 StaticObject s_obj(obj->getType(),
2173 obj->getBasePosition(), data);
2175 block->m_static_objects.insert(0, s_obj);
2181 last_node_walkable = false;
2184 block->setChangedFlag();
2187 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2190 This is the main map generation method
2193 void makeChunk(ChunkMakeData *data)
2198 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2199 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2200 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2201 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2202 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2203 *(u32)h_blocks*MAP_BLOCKSIZE;
2204 v3s16 bigarea_blocks_min(
2205 data->sectorpos_bigbase.X,
2207 data->sectorpos_bigbase.Y
2209 v3s16 bigarea_blocks_max(
2210 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2212 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2214 s16 lighting_min_d = 0-data->max_spread_amount;
2215 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2216 + data->max_spread_amount-1;
2219 data->vmanip.clearFlag(0xff);
2221 TimeTaker timer_generate("makeChunk() generate");
2223 // Maximum height of the stone surface and obstacles.
2224 // This is used to disable cave generation from going too high.
2225 s16 stone_surface_max_y = 0;
2228 Generate general ground level to full area
2233 //TimeTaker timer1("ground level");
2235 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2236 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2239 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2242 Skip of already generated
2245 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2246 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2250 // Ground height at this point
2251 float surface_y_f = 0.0;
2253 // Use perlin noise for ground height
2254 surface_y_f = base_rock_level_2d(data->seed, p2d);
2256 /*// Experimental stuff
2258 float a = highlands_level_2d(data->seed, p2d);
2263 // Convert to integer
2264 s16 surface_y = (s16)surface_y_f;
2267 if(surface_y > stone_surface_max_y)
2268 stone_surface_max_y = surface_y;
2271 Fill ground with stone
2274 // Use fast index incrementing
2275 v3s16 em = data->vmanip.m_area.getExtent();
2276 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2277 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2279 // Skip if already generated.
2280 // This is done here because there might be a cave at
2281 // any point in ground, which could look like it
2282 // wasn't generated.
2283 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2286 data->vmanip.m_data[i].d = CONTENT_STONE;
2288 data->vmanip.m_area.add_y(em, i, 1);
2296 Randomize some parameters
2299 //s32 stone_obstacle_count = 0;
2300 /*s32 stone_obstacle_count =
2301 rangelim((1.0+noise2d(data->seed+897,
2302 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2304 //s16 stone_obstacle_max_height = 0;
2305 /*s16 stone_obstacle_max_height =
2306 rangelim((1.0+noise2d(data->seed+5902,
2307 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2310 Loop this part, it will make stuff look older and newer nicely
2312 //for(u32 i_age=0; i_age<1; i_age++)
2313 for(u32 i_age=0; i_age<2; i_age++)
2315 /******************************
2316 BEGINNING OF AGING LOOP
2317 ******************************/
2321 //TimeTaker timer1("caves");
2326 u32 caves_count = relative_volume / 400000;
2327 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2328 if(stone_surface_max_y < WATER_LEVEL)
2330 /*u32 caves_count = 0;
2331 u32 bruises_count = 0;*/
2332 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2334 s16 min_tunnel_diameter = 3;
2335 s16 max_tunnel_diameter = 5;
2336 u16 tunnel_routepoints = 20;
2338 v3f main_direction(0,0,0);
2340 bool bruise_surface = (jj > caves_count);
2344 min_tunnel_diameter = 5;
2345 max_tunnel_diameter = myrand_range(10, 20);
2346 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2347 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2349 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2350 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2352 tunnel_routepoints = 5;
2358 // Allowed route area size in nodes
2360 data->sectorpos_base_size*MAP_BLOCKSIZE,
2361 h_blocks*MAP_BLOCKSIZE,
2362 data->sectorpos_base_size*MAP_BLOCKSIZE
2365 // Area starting point in nodes
2367 data->sectorpos_base.X*MAP_BLOCKSIZE,
2368 data->y_blocks_min*MAP_BLOCKSIZE,
2369 data->sectorpos_base.Y*MAP_BLOCKSIZE
2373 //(this should be more than the maximum radius of the tunnel)
2374 //s16 insure = 5; // Didn't work with max_d = 20
2376 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2377 ar += v3s16(1,0,1) * more * 2;
2378 of -= v3s16(1,0,1) * more;
2380 s16 route_y_min = 0;
2381 // Allow half a diameter + 7 over stone surface
2382 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2384 /*// If caves, don't go through surface too often
2385 if(bruise_surface == false)
2386 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2388 // Limit maximum to area
2389 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2393 /*// Minimum is at y=0
2394 route_y_min = -of.Y - 0;*/
2395 // Minimum is at y=max_tunnel_diameter/4
2396 //route_y_min = -of.Y + max_tunnel_diameter/4;
2397 //s16 min = -of.Y + max_tunnel_diameter/4;
2398 s16 min = -of.Y + 0;
2399 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2400 route_y_min = rangelim(route_y_min, 0, route_y_max);
2403 /*dstream<<"route_y_min = "<<route_y_min
2404 <<", route_y_max = "<<route_y_max<<std::endl;*/
2406 s16 route_start_y_min = route_y_min;
2407 s16 route_start_y_max = route_y_max;
2409 // Start every 2nd cave from surface
2410 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2412 if(coming_from_surface)
2414 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2417 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2418 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2420 // Randomize starting position
2422 (float)(myrand()%ar.X)+0.5,
2423 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2424 (float)(myrand()%ar.Z)+0.5
2427 MapNode airnode(CONTENT_AIR);
2430 Generate some tunnel starting from orp
2433 for(u16 j=0; j<tunnel_routepoints; j++)
2435 if(j%7==0 && bruise_surface == false)
2437 main_direction = v3f(
2438 ((float)(myrand()%20)-(float)10)/10,
2439 ((float)(myrand()%20)-(float)10)/30,
2440 ((float)(myrand()%20)-(float)10)/10
2442 main_direction *= (float)myrand_range(1, 3);
2446 s16 min_d = min_tunnel_diameter;
2447 s16 max_d = max_tunnel_diameter;
2448 s16 rs = myrand_range(min_d, max_d);
2453 maxlen = v3s16(rs*7,rs*7,rs*7);
2457 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2462 if(coming_from_surface && j < 3)
2465 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2466 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2467 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2473 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2474 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2475 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2479 vec += main_direction;
2484 else if(rp.X >= ar.X)
2486 if(rp.Y < route_y_min)
2488 else if(rp.Y >= route_y_max)
2489 rp.Y = route_y_max-1;
2492 else if(rp.Z >= ar.Z)
2496 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2498 v3f fp = orp + vec * f;
2499 v3s16 cp(fp.X, fp.Y, fp.Z);
2502 s16 d1 = d0 + rs - 1;
2503 for(s16 z0=d0; z0<=d1; z0++)
2505 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2506 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2507 for(s16 x0=-si; x0<=si-1; x0++)
2509 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2510 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2511 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2512 //s16 si2 = rs - abs(x0);
2513 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2519 /*if(isInArea(p, ar) == false)
2521 // Check only height
2522 if(y < 0 || y >= ar.Y)
2526 //assert(data->vmanip.m_area.contains(p));
2527 if(data->vmanip.m_area.contains(p) == false)
2529 dstream<<"WARNING: "<<__FUNCTION_NAME
2530 <<":"<<__LINE__<<": "
2531 <<"point not in area"
2536 // Just set it to air, it will be changed to
2538 u32 i = data->vmanip.m_area.index(p);
2539 data->vmanip.m_data[i] = airnode;
2541 if(bruise_surface == false)
2544 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2559 //TimeTaker timer1("ore veins");
2564 for(u32 jj=0; jj<relative_volume/1000; jj++)
2566 s16 max_vein_diameter = 3;
2568 // Allowed route area size in nodes
2570 data->sectorpos_base_size*MAP_BLOCKSIZE,
2571 h_blocks*MAP_BLOCKSIZE,
2572 data->sectorpos_base_size*MAP_BLOCKSIZE
2575 // Area starting point in nodes
2577 data->sectorpos_base.X*MAP_BLOCKSIZE,
2578 data->y_blocks_min*MAP_BLOCKSIZE,
2579 data->sectorpos_base.Y*MAP_BLOCKSIZE
2583 //(this should be more than the maximum radius of the tunnel)
2585 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2586 ar += v3s16(1,0,1) * more * 2;
2587 of -= v3s16(1,0,1) * more;
2589 // Randomize starting position
2591 (float)(myrand()%ar.X)+0.5,
2592 (float)(myrand()%ar.Y)+0.5,
2593 (float)(myrand()%ar.Z)+0.5
2596 // Randomize mineral
2599 mineral = MINERAL_COAL;
2601 mineral = MINERAL_IRON;
2604 Generate some vein starting from orp
2607 for(u16 j=0; j<2; j++)
2610 (float)(myrand()%ar.X)+0.5,
2611 (float)(myrand()%ar.Y)+0.5,
2612 (float)(myrand()%ar.Z)+0.5
2614 v3f vec = rp - orp;*/
2616 v3s16 maxlen(5, 5, 5);
2618 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2619 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2620 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2625 else if(rp.X >= ar.X)
2629 else if(rp.Y >= ar.Y)
2633 else if(rp.Z >= ar.Z)
2639 s16 max_d = max_vein_diameter;
2640 s16 rs = myrand_range(min_d, max_d);
2642 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2644 v3f fp = orp + vec * f;
2645 v3s16 cp(fp.X, fp.Y, fp.Z);
2647 s16 d1 = d0 + rs - 1;
2648 for(s16 z0=d0; z0<=d1; z0++)
2650 s16 si = rs - abs(z0);
2651 for(s16 x0=-si; x0<=si-1; x0++)
2653 s16 si2 = rs - abs(x0);
2654 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2656 // Don't put mineral to every place
2664 /*if(isInArea(p, ar) == false)
2666 // Check only height
2667 if(y < 0 || y >= ar.Y)
2671 assert(data->vmanip.m_area.contains(p));
2673 // Just set it to air, it will be changed to
2675 u32 i = data->vmanip.m_area.index(p);
2676 MapNode *n = &data->vmanip.m_data[i];
2677 if(n->d == CONTENT_STONE)
2692 //TimeTaker timer1("add mud");
2695 Add mud to the central chunk
2698 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2699 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2701 // Node position in 2d
2702 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2704 // Randomize mud amount
2705 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2706 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2707 data->seed+1, 3, 0.55));
2709 // Find ground level
2710 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2713 If topmost node is grass, change it to mud.
2714 It might be if it was flown to there from a neighboring
2715 chunk and then converted.
2718 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2719 MapNode *n = &data->vmanip.m_data[i];
2720 if(n->d == CONTENT_GRASS)
2729 v3s16 em = data->vmanip.m_area.getExtent();
2730 s16 y_start = surface_y+1;
2731 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2732 for(s16 y=y_start; y<=y_nodes_max; y++)
2734 if(mudcount >= mud_add_amount)
2737 MapNode &n = data->vmanip.m_data[i];
2741 data->vmanip.m_area.add_y(em, i, 1);
2750 TimeTaker timer1("flow mud");
2753 Flow mud away from steep edges
2756 // Limit area by 1 because mud is flown into neighbors.
2757 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2758 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2760 // Iterate a few times
2761 for(s16 k=0; k<3; k++)
2764 for(s16 x=mudflow_minpos;
2767 for(s16 z=mudflow_minpos;
2771 // Invert coordinates every 2nd iteration
2774 x = mudflow_maxpos - (x-mudflow_minpos);
2775 z = mudflow_maxpos - (z-mudflow_minpos);
2778 // Node position in 2d
2779 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2781 v3s16 em = data->vmanip.m_area.getExtent();
2782 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2789 for(; y>=y_nodes_min; y--)
2791 n = &data->vmanip.m_data[i];
2792 //if(content_walkable(n->d))
2794 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2797 data->vmanip.m_area.add_y(em, i, -1);
2800 // Stop if out of area
2801 //if(data->vmanip.m_area.contains(i) == false)
2805 /*// If not mud, do nothing to it
2806 MapNode *n = &data->vmanip.m_data[i];
2807 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2811 Don't flow it if the stuff under it is not mud
2815 data->vmanip.m_area.add_y(em, i2, -1);
2816 // Cancel if out of area
2817 if(data->vmanip.m_area.contains(i2) == false)
2819 MapNode *n2 = &data->vmanip.m_data[i2];
2820 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2824 // Make it exactly mud
2827 /*s16 recurse_count = 0;
2831 v3s16(0,0,1), // back
2832 v3s16(1,0,0), // right
2833 v3s16(0,0,-1), // front
2834 v3s16(-1,0,0), // left
2837 // Theck that upper is air or doesn't exist.
2838 // Cancel dropping if upper keeps it in place
2840 data->vmanip.m_area.add_y(em, i3, 1);
2841 if(data->vmanip.m_area.contains(i3) == true
2842 && content_walkable(data->vmanip.m_data[i3].d) == true)
2849 for(u32 di=0; di<4; di++)
2851 v3s16 dirp = dirs4[di];
2854 data->vmanip.m_area.add_p(em, i2, dirp);
2855 // Fail if out of area
2856 if(data->vmanip.m_area.contains(i2) == false)
2858 // Check that side is air
2859 MapNode *n2 = &data->vmanip.m_data[i2];
2860 if(content_walkable(n2->d))
2862 // Check that under side is air
2863 data->vmanip.m_area.add_y(em, i2, -1);
2864 if(data->vmanip.m_area.contains(i2) == false)
2866 n2 = &data->vmanip.m_data[i2];
2867 if(content_walkable(n2->d))
2869 /*// Check that under that is air (need a drop of 2)
2870 data->vmanip.m_area.add_y(em, i2, -1);
2871 if(data->vmanip.m_area.contains(i2) == false)
2873 n2 = &data->vmanip.m_data[i2];
2874 if(content_walkable(n2->d))
2876 // Loop further down until not air
2878 data->vmanip.m_area.add_y(em, i2, -1);
2879 // Fail if out of area
2880 if(data->vmanip.m_area.contains(i2) == false)
2882 n2 = &data->vmanip.m_data[i2];
2883 }while(content_walkable(n2->d) == false);
2884 // Loop one up so that we're in air
2885 data->vmanip.m_area.add_y(em, i2, 1);
2886 n2 = &data->vmanip.m_data[i2];
2888 // Move mud to new place
2890 // Set old place to be air
2891 *n = MapNode(CONTENT_AIR);
2904 //TimeTaker timer1("add water");
2907 Add water to the central chunk (and a bit more)
2910 for(s16 x=0-data->max_spread_amount;
2911 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2913 for(s16 z=0-data->max_spread_amount;
2914 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2917 // Node position in 2d
2918 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2920 // Find ground level
2921 //s16 surface_y = find_ground_level(data->vmanip, p2d);
2924 If ground level is over water level, skip.
2925 NOTE: This leaves caves near water without water,
2926 which looks especially crappy when the nearby water
2927 won't start flowing either for some reason
2929 /*if(surface_y > WATER_LEVEL)
2936 v3s16 em = data->vmanip.m_area.getExtent();
2937 u8 light = LIGHT_MAX;
2938 // Start at global water surface level
2939 s16 y_start = WATER_LEVEL;
2940 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2941 MapNode *n = &data->vmanip.m_data[i];
2943 for(s16 y=y_start; y>=y_nodes_min; y--)
2945 n = &data->vmanip.m_data[i];
2947 // Stop when there is no water and no air
2948 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2949 && n->d != CONTENT_WATER)
2955 // Make water only not in caves
2956 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
2958 n->d = CONTENT_WATERSOURCE;
2959 //n->setLight(LIGHTBANK_DAY, light);
2961 // Add to transforming liquid queue (in case it'd
2963 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2964 data->transforming_liquid.push_back(p);
2968 data->vmanip.m_area.add_y(em, i, -1);
2979 /***********************
2981 ************************/
2984 //TimeTaker timer1("convert mud to sand");
2990 //s16 mud_add_amount = myrand_range(2, 4);
2991 //s16 mud_add_amount = 0;
2993 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2994 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
2995 for(s16 x=0-data->max_spread_amount+1;
2996 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
2998 for(s16 z=0-data->max_spread_amount+1;
2999 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3002 // Node position in 2d
3003 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3005 // Determine whether to have sand here
3006 double sandnoise = noise2d_perlin(
3007 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3008 data->seed+59420, 3, 0.50);
3010 bool have_sand = (sandnoise > -0.15);
3012 if(have_sand == false)
3015 // Find ground level
3016 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3018 if(surface_y > WATER_LEVEL + 2)
3022 v3s16 em = data->vmanip.m_area.getExtent();
3023 s16 y_start = surface_y;
3024 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3025 u32 not_sand_counter = 0;
3026 for(s16 y=y_start; y>=y_nodes_min; y--)
3028 MapNode *n = &data->vmanip.m_data[i];
3029 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3031 n->d = CONTENT_SAND;
3036 if(not_sand_counter > 3)
3040 data->vmanip.m_area.add_y(em, i, -1);
3049 //TimeTaker timer1("generate trees");
3055 // Divide area into parts
3057 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3058 double area = sidelen * sidelen;
3059 for(s16 x0=0; x0<div; x0++)
3060 for(s16 z0=0; z0<div; z0++)
3062 // Center position of part of division
3064 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3065 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3067 // Minimum edge of part of division
3069 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3070 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3072 // Maximum edge of part of division
3074 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3075 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3078 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3079 // Put trees in random places on part of division
3080 for(u32 i=0; i<tree_count; i++)
3082 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3083 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3084 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3085 // Don't make a tree under water level
3088 // Don't make a tree so high that it doesn't fit
3089 if(y > y_nodes_max - 6)
3093 Trees grow only on mud and grass
3096 u32 i = data->vmanip.m_area.index(v3s16(p));
3097 MapNode *n = &data->vmanip.m_data[i];
3098 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3103 make_tree(data->vmanip, p);
3106 /*u32 tree_max = relative_area / 60;
3107 //u32 count = myrand_range(0, tree_max);
3108 for(u32 i=0; i<count; i++)
3110 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3111 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3112 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3113 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3114 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3115 // Don't make a tree under water level
3120 make_tree(data->vmanip, p);
3128 //TimeTaker timer1("grow grass");
3134 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3135 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3136 for(s16 x=0-data->max_spread_amount;
3137 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3139 for(s16 z=0-data->max_spread_amount;
3140 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3143 // Node position in 2d
3144 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3147 Find the lowest surface to which enough light ends up
3150 Basically just wait until not air and not leaves.
3154 v3s16 em = data->vmanip.m_area.getExtent();
3155 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3157 // Go to ground level
3158 for(y=y_nodes_max; y>=y_nodes_min; y--)
3160 MapNode &n = data->vmanip.m_data[i];
3161 if(n.d != CONTENT_AIR
3162 && n.d != CONTENT_LEAVES)
3164 data->vmanip.m_area.add_y(em, i, -1);
3166 if(y >= y_nodes_min)
3169 surface_y = y_nodes_min;
3172 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3173 MapNode *n = &data->vmanip.m_data[i];
3174 if(n->d == CONTENT_MUD)
3175 n->d = CONTENT_GRASS;
3181 Initial lighting (sunlight)
3184 core::map<v3s16, bool> light_sources;
3187 // 750ms @cs=8, can't optimize more
3188 TimeTaker timer1("initial lighting");
3192 Go through the edges and add all nodes that have light to light_sources
3196 for(s16 i=0; i<4; i++)
3198 for(s16 j=lighting_min_d;
3205 if(i == 0 || i == 1)
3207 x = (i==0) ? lighting_min_d : lighting_max_d;
3216 z = (i==0) ? lighting_min_d : lighting_max_d;
3223 // Node position in 2d
3224 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3227 v3s16 em = data->vmanip.m_area.getExtent();
3228 s16 y_start = y_nodes_max;
3229 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3230 for(s16 y=y_start; y>=y_nodes_min; y--)
3232 MapNode *n = &data->vmanip.m_data[i];
3233 if(n->getLight(LIGHTBANK_DAY) != 0)
3235 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3237 //NOTE: This is broken, at least the index has to
3246 Go through the edges and apply sunlight to them, not caring
3251 for(s16 i=0; i<4; i++)
3253 for(s16 j=lighting_min_d;
3260 if(i == 0 || i == 1)
3262 x = (i==0) ? lighting_min_d : lighting_max_d;
3271 z = (i==0) ? lighting_min_d : lighting_max_d;
3278 // Node position in 2d
3279 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3281 // Loop from top to down
3283 u8 light = LIGHT_SUN;
3284 v3s16 em = data->vmanip.m_area.getExtent();
3285 s16 y_start = y_nodes_max;
3286 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3287 for(s16 y=y_start; y>=y_nodes_min; y--)
3289 MapNode *n = &data->vmanip.m_data[i];
3290 if(light_propagates_content(n->d) == false)
3294 else if(light != LIGHT_SUN
3295 || sunlight_propagates_content(n->d) == false)
3301 n->setLight(LIGHTBANK_DAY, light);
3302 n->setLight(LIGHTBANK_NIGHT, 0);
3306 // Insert light source
3307 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3310 // Increment index by y
3311 data->vmanip.m_area.add_y(em, i, -1);
3317 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3318 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3319 /*for(s16 x=0-data->max_spread_amount+1;
3320 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3322 for(s16 z=0-data->max_spread_amount+1;
3323 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3327 This has to be 1 smaller than the actual area, because
3328 neighboring nodes are checked.
3330 for(s16 x=lighting_min_d+1;
3331 x<=lighting_max_d-1;
3333 for(s16 z=lighting_min_d+1;
3334 z<=lighting_max_d-1;
3337 // Node position in 2d
3338 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3341 Apply initial sunlight
3344 u8 light = LIGHT_SUN;
3345 bool add_to_sources = false;
3346 v3s16 em = data->vmanip.m_area.getExtent();
3347 s16 y_start = y_nodes_max;
3348 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3349 for(s16 y=y_start; y>=y_nodes_min; y--)
3351 MapNode *n = &data->vmanip.m_data[i];
3353 if(light_propagates_content(n->d) == false)
3357 else if(light != LIGHT_SUN
3358 || sunlight_propagates_content(n->d) == false)
3364 // This doesn't take much time
3365 if(add_to_sources == false)
3368 Check sides. If side is not air or water, start
3369 adding to light_sources.
3372 v3s16(0,0,1), // back
3373 v3s16(1,0,0), // right
3374 v3s16(0,0,-1), // front
3375 v3s16(-1,0,0), // left
3377 for(u32 di=0; di<4; di++)
3379 v3s16 dirp = dirs4[di];
3381 data->vmanip.m_area.add_p(em, i2, dirp);
3382 MapNode *n2 = &data->vmanip.m_data[i2];
3384 n2->d != CONTENT_AIR
3385 && n2->d != CONTENT_WATERSOURCE
3386 && n2->d != CONTENT_WATER
3388 add_to_sources = true;
3394 n->setLight(LIGHTBANK_DAY, light);
3395 n->setLight(LIGHTBANK_NIGHT, 0);
3397 // This doesn't take much time
3398 if(light != 0 && add_to_sources)
3400 // Insert light source
3401 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3404 // Increment index by y
3405 data->vmanip.m_area.add_y(em, i, -1);
3413 // Spread light around
3415 TimeTaker timer("makeChunk() spreadLight");
3416 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3423 timer_generate.stop();
3426 //###################################################################
3427 //###################################################################
3428 //###################################################################
3429 //###################################################################
3430 //###################################################################
3431 //###################################################################
3432 //###################################################################
3433 //###################################################################
3434 //###################################################################
3435 //###################################################################
3436 //###################################################################
3437 //###################################################################
3438 //###################################################################
3439 //###################################################################
3440 //###################################################################
3442 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3444 if(m_chunksize == 0)
3452 // The distance how far into the neighbors the generator is allowed to go.
3453 s16 max_spread_amount_sectors = 2;
3454 assert(max_spread_amount_sectors <= m_chunksize);
3455 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3457 s16 y_blocks_min = -4;
3458 s16 y_blocks_max = 3;
3460 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3461 s16 sectorpos_base_size = m_chunksize;
3463 v2s16 sectorpos_bigbase =
3464 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3465 s16 sectorpos_bigbase_size =
3466 sectorpos_base_size + 2 * max_spread_amount_sectors;
3469 data.chunkpos = chunkpos;
3470 data.y_blocks_min = y_blocks_min;
3471 data.y_blocks_max = y_blocks_max;
3472 data.sectorpos_base = sectorpos_base;
3473 data.sectorpos_base_size = sectorpos_base_size;
3474 data.sectorpos_bigbase = sectorpos_bigbase;
3475 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3476 data.max_spread_amount = max_spread_amount;
3479 Create the whole area of this and the neighboring chunks
3482 TimeTaker timer("initChunkMake() create area");
3484 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3485 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3487 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3488 ServerMapSector *sector = createSector(sectorpos);
3491 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3493 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3494 MapBlock *block = createBlock(blockpos);
3496 // Lighting won't be calculated
3497 //block->setLightingExpired(true);
3498 // Lighting will be calculated
3499 block->setLightingExpired(false);
3502 Block gets sunlight if this is true.
3504 This should be set to true when the top side of a block
3505 is completely exposed to the sky.
3507 Actually this doesn't matter now because the
3508 initial lighting is done here.
3510 block->setIsUnderground(y != y_blocks_max);
3516 Now we have a big empty area.
3518 Make a ManualMapVoxelManipulator that contains this and the
3522 v3s16 bigarea_blocks_min(
3523 sectorpos_bigbase.X,
3527 v3s16 bigarea_blocks_max(
3528 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3530 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3533 data.vmanip.setMap(this);
3536 TimeTaker timer("initChunkMake() initialEmerge");
3537 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3542 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3543 core::map<v3s16, MapBlock*> &changed_blocks)
3549 Blit generated stuff to map
3553 //TimeTaker timer("generateChunkRaw() blitBackAll");
3554 data.vmanip.blitBackAll(&changed_blocks);
3558 Update day/night difference cache of the MapBlocks
3561 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3562 i.atEnd() == false; i++)
3564 MapBlock *block = i.getNode()->getValue();
3565 block->updateDayNightDiff();
3570 Copy transforming liquid information
3572 while(data.transforming_liquid.size() > 0)
3574 v3s16 p = data.transforming_liquid.pop_front();
3575 m_transforming_liquid.push_back(p);
3579 Add random objects to blocks
3582 for(s16 x=0; x<data.sectorpos_base_size; x++)
3583 for(s16 z=0; z<data.sectorpos_base_size; z++)
3585 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3586 ServerMapSector *sector = createSector(sectorpos);
3589 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3591 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3592 MapBlock *block = createBlock(blockpos);
3593 addRandomObjects(block);
3599 Create chunk metadata
3602 for(s16 x=-1; x<=1; x++)
3603 for(s16 y=-1; y<=1; y++)
3605 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3606 // Add chunk meta information
3607 MapChunk *chunk = getChunk(chunkpos0);
3610 chunk = new MapChunk();
3611 m_chunks.insert(chunkpos0, chunk);
3613 //chunk->setIsVolatile(true);
3614 if(chunk->getGenLevel() > GENERATED_PARTLY)
3615 chunk->setGenLevel(GENERATED_PARTLY);
3619 Set central chunk non-volatile
3621 MapChunk *chunk = getChunk(data.chunkpos);
3624 //chunk->setIsVolatile(false);
3625 chunk->setGenLevel(GENERATED_FULLY);
3628 Save changed parts of map
3637 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3638 core::map<v3s16, MapBlock*> &changed_blocks,
3641 DSTACK(__FUNCTION_NAME);
3644 Don't generate if already fully generated
3648 MapChunk *chunk = getChunk(chunkpos);
3649 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3651 dstream<<"generateChunkRaw(): Chunk "
3652 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3653 <<" already generated"<<std::endl;
3658 dstream<<"generateChunkRaw(): Generating chunk "
3659 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3662 TimeTaker timer("generateChunkRaw()");
3666 // Initialize generation
3667 initChunkMake(data, chunkpos);
3672 // Finalize generation
3673 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3676 Return central chunk (which was requested)
3682 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3683 core::map<v3s16, MapBlock*> &changed_blocks)
3685 dstream<<"generateChunk(): Generating chunk "
3686 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3689 /*for(s16 x=-1; x<=1; x++)
3690 for(s16 y=-1; y<=1; y++)*/
3691 for(s16 x=-0; x<=0; x++)
3692 for(s16 y=-0; y<=0; y++)
3694 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3695 MapChunk *chunk = getChunk(chunkpos0);
3696 // Skip if already generated
3697 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3699 generateChunkRaw(chunkpos0, changed_blocks);
3702 assert(chunkNonVolatile(chunkpos1));
3704 MapChunk *chunk = getChunk(chunkpos1);
3709 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3711 DSTACK("%s: p2d=(%d,%d)",
3716 Check if it exists already in memory
3718 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3723 Try to load it from disk (with blocks)
3725 if(loadSectorFull(p2d) == true)
3727 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3730 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3731 throw InvalidPositionException("");
3737 Do not create over-limit
3739 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3740 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3741 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3742 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3743 throw InvalidPositionException("createSector(): pos. over limit");
3746 Generate blank sector
3749 sector = new ServerMapSector(this, p2d);
3751 // Sector position on map in nodes
3752 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3757 m_sectors.insert(p2d, sector);
3763 MapSector * ServerMap::emergeSector(v2s16 p2d,
3764 core::map<v3s16, MapBlock*> &changed_blocks)
3766 DSTACK("%s: p2d=(%d,%d)",
3773 v2s16 chunkpos = sector_to_chunk(p2d);
3774 /*bool chunk_nonvolatile = false;
3775 MapChunk *chunk = getChunk(chunkpos);
3776 if(chunk && chunk->getIsVolatile() == false)
3777 chunk_nonvolatile = true;*/
3778 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3781 If chunk is not fully generated, generate chunk
3783 if(chunk_nonvolatile == false)
3785 // Generate chunk and neighbors
3786 generateChunk(chunkpos, changed_blocks);
3790 Return sector if it exists now
3792 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3797 Try to load it from disk
3799 if(loadSectorFull(p2d) == true)
3801 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3804 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3805 throw InvalidPositionException("");
3811 generateChunk should have generated the sector
3815 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3816 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3820 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3822 return createSector(p2d);
3827 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3830 generateChunkRaw(chunkpos, changed_blocks, true);
3833 Return sector if it exists now
3835 sector = getSectorNoGenerateNoEx(p2d);
3839 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3847 //return generateSector();
3852 NOTE: This is not used for main map generation, only for blocks
3853 that are very high or low
3855 MapBlock * ServerMap::generateBlock(
3857 MapBlock *original_dummy,
3858 ServerMapSector *sector,
3859 core::map<v3s16, MapBlock*> &changed_blocks,
3860 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3863 DSTACK("%s: p=(%d,%d,%d)",
3867 // If chunks are disabled
3868 /*if(m_chunksize == 0)
3870 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
3871 <<"not generating."<<std::endl;
3875 /*dstream<<"generateBlock(): "
3876 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3879 MapBlock *block = original_dummy;
3881 v2s16 p2d(p.X, p.Z);
3883 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3886 Do not generate over-limit
3888 if(blockpos_over_limit(p))
3890 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3891 throw InvalidPositionException("generateBlock(): pos. over limit");
3895 If block doesn't exist, create one.
3896 If it exists, it is a dummy. In that case unDummify() it.
3898 NOTE: This already sets the map as the parent of the block
3902 block = sector->createBlankBlockNoInsert(block_y);
3906 // Remove the block so that nobody can get a half-generated one.
3907 sector->removeBlock(block);
3908 // Allocate the block to contain the generated data
3912 u8 water_material = CONTENT_WATERSOURCE;
3914 s32 lowest_ground_y = 32767;
3915 s32 highest_ground_y = -32768;
3917 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3918 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3920 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3922 //s16 surface_y = 0;
3924 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3925 + AVERAGE_MUD_AMOUNT;
3926 // If chunks are disabled
3927 if(m_chunksize == 0)
3928 surface_y = WATER_LEVEL + 1;
3930 if(surface_y < lowest_ground_y)
3931 lowest_ground_y = surface_y;
3932 if(surface_y > highest_ground_y)
3933 highest_ground_y = surface_y;
3935 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3937 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3939 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3944 NOTE: If there are some man-made structures above the
3945 newly created block, they won't be taken into account.
3947 if(real_y > surface_y)
3948 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3954 // If node is over heightmap y, it's air or water
3955 if(real_y > surface_y)
3957 // If under water level, it's water
3958 if(real_y < WATER_LEVEL)
3960 n.d = water_material;
3961 n.setLight(LIGHTBANK_DAY,
3962 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3964 Add to transforming liquid queue (in case it'd
3967 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3968 m_transforming_liquid.push_back(real_pos);
3974 // Else it's ground or caves (air)
3977 // If it's surface_depth under ground, it's stone
3978 if(real_y <= surface_y - surface_depth)
3980 n.d = CONTENT_STONE;
3984 // It is mud if it is under the first ground
3985 // level or under water
3986 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3992 n.d = CONTENT_GRASS;
3995 //n.d = CONTENT_MUD;
3997 /*// If under water level, it's mud
3998 if(real_y < WATER_LEVEL)
4000 // Only the topmost node is grass
4001 else if(real_y <= surface_y - 1)
4004 n.d = CONTENT_GRASS;*/
4008 block->setNode(v3s16(x0,y0,z0), n);
4013 Calculate some helper variables
4016 // Completely underground if the highest part of block is under lowest
4018 // This has to be very sure; it's probably one too strict now but
4019 // that's just better.
4020 bool completely_underground =
4021 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4023 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4025 bool mostly_underwater_surface = false;
4026 if(highest_ground_y < WATER_LEVEL
4027 && some_part_underground && !completely_underground)
4028 mostly_underwater_surface = true;
4031 Get local attributes
4034 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4036 float caves_amount = 0.5;
4041 NOTE: BEWARE: Too big amount of attribute points slows verything
4043 1 interpolation from 5000 points takes 2-3ms.
4045 //TimeTaker timer("generateBlock() local attribute retrieval");
4046 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4047 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4048 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4052 //dstream<<"generateBlock(): Done"<<std::endl;
4058 // Initialize temporary table
4059 const s32 ued = MAP_BLOCKSIZE;
4060 bool underground_emptiness[ued*ued*ued];
4061 for(s32 i=0; i<ued*ued*ued; i++)
4063 underground_emptiness[i] = 0;
4070 Initialize orp and ors. Try to find if some neighboring
4071 MapBlock has a tunnel ended in its side
4075 (float)(myrand()%ued)+0.5,
4076 (float)(myrand()%ued)+0.5,
4077 (float)(myrand()%ued)+0.5
4080 bool found_existing = false;
4086 for(s16 y=0; y<ued; y++)
4087 for(s16 x=0; x<ued; x++)
4089 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4090 if(getNode(ap).d == CONTENT_AIR)
4092 orp = v3f(x+1,y+1,0);
4093 found_existing = true;
4094 goto continue_generating;
4098 catch(InvalidPositionException &e){}
4104 for(s16 y=0; y<ued; y++)
4105 for(s16 x=0; x<ued; x++)
4107 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4108 if(getNode(ap).d == CONTENT_AIR)
4110 orp = v3f(x+1,y+1,ued-1);
4111 found_existing = true;
4112 goto continue_generating;
4116 catch(InvalidPositionException &e){}
4122 for(s16 y=0; y<ued; y++)
4123 for(s16 z=0; z<ued; z++)
4125 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4126 if(getNode(ap).d == CONTENT_AIR)
4128 orp = v3f(0,y+1,z+1);
4129 found_existing = true;
4130 goto continue_generating;
4134 catch(InvalidPositionException &e){}
4140 for(s16 y=0; y<ued; y++)
4141 for(s16 z=0; z<ued; z++)
4143 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4144 if(getNode(ap).d == CONTENT_AIR)
4146 orp = v3f(ued-1,y+1,z+1);
4147 found_existing = true;
4148 goto continue_generating;
4152 catch(InvalidPositionException &e){}
4158 for(s16 x=0; x<ued; x++)
4159 for(s16 z=0; z<ued; z++)
4161 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4162 if(getNode(ap).d == CONTENT_AIR)
4164 orp = v3f(x+1,0,z+1);
4165 found_existing = true;
4166 goto continue_generating;
4170 catch(InvalidPositionException &e){}
4176 for(s16 x=0; x<ued; x++)
4177 for(s16 z=0; z<ued; z++)
4179 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4180 if(getNode(ap).d == CONTENT_AIR)
4182 orp = v3f(x+1,ued-1,z+1);
4183 found_existing = true;
4184 goto continue_generating;
4188 catch(InvalidPositionException &e){}
4190 continue_generating:
4193 Choose whether to actually generate cave
4195 bool do_generate_caves = true;
4196 // Don't generate if no part is underground
4197 if(!some_part_underground)
4199 do_generate_caves = false;
4201 // Don't generate if mostly underwater surface
4202 /*else if(mostly_underwater_surface)
4204 do_generate_caves = false;
4206 // Partly underground = cave
4207 else if(!completely_underground)
4209 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4211 // Found existing cave underground
4212 else if(found_existing && completely_underground)
4214 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4216 // Underground and no caves found
4219 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4222 if(do_generate_caves)
4225 Generate some tunnel starting from orp and ors
4227 for(u16 i=0; i<3; i++)
4230 (float)(myrand()%ued)+0.5,
4231 (float)(myrand()%ued)+0.5,
4232 (float)(myrand()%ued)+0.5
4236 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4240 for(float f=0; f<1.0; f+=0.04)
4242 v3f fp = orp + vec * f;
4243 v3s16 cp(fp.X, fp.Y, fp.Z);
4245 s16 d1 = d0 + rs - 1;
4246 for(s16 z0=d0; z0<=d1; z0++)
4248 s16 si = rs - abs(z0);
4249 for(s16 x0=-si; x0<=si-1; x0++)
4251 s16 si2 = rs - abs(x0);
4252 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4258 if(isInArea(p, ued) == false)
4260 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4272 // Set to true if has caves.
4273 // Set when some non-air is changed to air when making caves.
4274 bool has_caves = false;
4277 Apply temporary cave data to block
4280 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4281 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4283 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4285 MapNode n = block->getNode(v3s16(x0,y0,z0));
4288 if(underground_emptiness[
4289 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4290 +ued*(y0*ued/MAP_BLOCKSIZE)
4291 +(x0*ued/MAP_BLOCKSIZE)])
4293 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4302 block->setNode(v3s16(x0,y0,z0), n);
4307 This is used for guessing whether or not the block should
4308 receive sunlight from the top if the block above doesn't exist
4310 block->setIsUnderground(completely_underground);
4313 Force lighting update if some part of block is partly
4314 underground and has caves.
4316 /*if(some_part_underground && !completely_underground && has_caves)
4318 //dstream<<"Half-ground caves"<<std::endl;
4319 lighting_invalidated_blocks[block->getPos()] = block;
4322 // DEBUG: Always update lighting
4323 //lighting_invalidated_blocks[block->getPos()] = block;
4329 if(some_part_underground)
4331 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4336 for(s16 i=0; i<underground_level/4 + 1; i++)
4338 if(myrand()%50 == 0)
4341 (myrand()%(MAP_BLOCKSIZE-2))+1,
4342 (myrand()%(MAP_BLOCKSIZE-2))+1,
4343 (myrand()%(MAP_BLOCKSIZE-2))+1
4349 for(u16 i=0; i<27; i++)
4351 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4353 block->setNode(cp+g_27dirs[i], n);
4361 u16 coal_amount = 30;
4362 u16 coal_rareness = 60 / coal_amount;
4363 if(coal_rareness == 0)
4365 if(myrand()%coal_rareness == 0)
4367 u16 a = myrand() % 16;
4368 u16 amount = coal_amount * a*a*a / 1000;
4369 for(s16 i=0; i<amount; i++)
4372 (myrand()%(MAP_BLOCKSIZE-2))+1,
4373 (myrand()%(MAP_BLOCKSIZE-2))+1,
4374 (myrand()%(MAP_BLOCKSIZE-2))+1
4378 n.d = CONTENT_STONE;
4379 n.param = MINERAL_COAL;
4381 for(u16 i=0; i<27; i++)
4383 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4385 block->setNode(cp+g_27dirs[i], n);
4393 //TODO: change to iron_amount or whatever
4394 u16 iron_amount = 15;
4395 u16 iron_rareness = 60 / iron_amount;
4396 if(iron_rareness == 0)
4398 if(myrand()%iron_rareness == 0)
4400 u16 a = myrand() % 16;
4401 u16 amount = iron_amount * a*a*a / 1000;
4402 for(s16 i=0; i<amount; i++)
4405 (myrand()%(MAP_BLOCKSIZE-2))+1,
4406 (myrand()%(MAP_BLOCKSIZE-2))+1,
4407 (myrand()%(MAP_BLOCKSIZE-2))+1
4411 n.d = CONTENT_STONE;
4412 n.param = MINERAL_IRON;
4414 for(u16 i=0; i<27; i++)
4416 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4418 block->setNode(cp+g_27dirs[i], n);
4425 Create a few rats in empty blocks underground
4427 if(completely_underground)
4429 //for(u16 i=0; i<2; i++)
4432 (myrand()%(MAP_BLOCKSIZE-2))+1,
4433 (myrand()%(MAP_BLOCKSIZE-2))+1,
4434 (myrand()%(MAP_BLOCKSIZE-2))+1
4437 // Check that the place is empty
4438 //if(!is_ground_content(block->getNode(cp).d))
4441 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4442 block->addObject(obj);
4448 Add block to sector.
4450 sector->insertBlock(block);
4452 // Lighting is invalid after generation.
4453 block->setLightingExpired(true);
4460 <<"lighting_invalidated_blocks.size()"
4464 <<" "<<lighting_invalidated_blocks.size()
4466 <<", "<<completely_underground
4467 <<", "<<some_part_underground
4474 MapBlock * ServerMap::createBlock(v3s16 p)
4476 DSTACK("%s: p=(%d,%d,%d)",
4477 __FUNCTION_NAME, p.X, p.Y, p.Z);
4480 Do not create over-limit
4482 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4483 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4484 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4485 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4486 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4487 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4488 throw InvalidPositionException("createBlock(): pos. over limit");
4490 v2s16 p2d(p.X, p.Z);
4493 This will create or load a sector if not found in memory.
4494 If block exists on disk, it will be loaded.
4496 NOTE: On old save formats, this will be slow, as it generates
4497 lighting on blocks for them.
4499 ServerMapSector *sector;
4501 sector = (ServerMapSector*)createSector(p2d);
4502 assert(sector->getId() == MAPSECTOR_SERVER);
4504 catch(InvalidPositionException &e)
4506 dstream<<"createBlock: createSector() failed"<<std::endl;
4510 NOTE: This should not be done, or at least the exception
4511 should not be passed on as std::exception, because it
4512 won't be catched at all.
4514 /*catch(std::exception &e)
4516 dstream<<"createBlock: createSector() failed: "
4517 <<e.what()<<std::endl;
4522 Try to get a block from the sector
4525 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4529 block = sector->createBlankBlock(block_y);
4533 MapBlock * ServerMap::emergeBlock(
4535 bool only_from_disk,
4536 core::map<v3s16, MapBlock*> &changed_blocks,
4537 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4540 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4542 p.X, p.Y, p.Z, only_from_disk);
4545 Do not generate over-limit
4547 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4548 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4549 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4550 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4551 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4552 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4553 throw InvalidPositionException("emergeBlock(): pos. over limit");
4555 v2s16 p2d(p.X, p.Z);
4558 This will create or load a sector if not found in memory.
4559 If block exists on disk, it will be loaded.
4561 ServerMapSector *sector;
4563 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4564 assert(sector->getId() == MAPSECTOR_SERVER);
4566 catch(InvalidPositionException &e)
4568 dstream<<"emergeBlock: emergeSector() failed: "
4569 <<e.what()<<std::endl;
4570 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4572 <<"You could try to delete it."<<std::endl;
4575 catch(VersionMismatchException &e)
4577 dstream<<"emergeBlock: emergeSector() failed: "
4578 <<e.what()<<std::endl;
4579 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4581 <<"You could try to delete it."<<std::endl;
4585 NOTE: This should not be done, or at least the exception
4586 should not be passed on as std::exception, because it
4587 won't be catched at all.
4589 /*catch(std::exception &e)
4591 dstream<<"emergeBlock: emergeSector() failed: "
4592 <<e.what()<<std::endl;
4593 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4595 <<"You could try to delete it."<<std::endl;
4600 Try to get a block from the sector
4603 bool does_not_exist = false;
4604 bool lighting_expired = false;
4605 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4609 does_not_exist = true;
4611 else if(block->isDummy() == true)
4613 does_not_exist = true;
4615 else if(block->getLightingExpired())
4617 lighting_expired = true;
4622 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4627 If block was not found on disk and not going to generate a
4628 new one, make sure there is a dummy block in place.
4630 if(only_from_disk && (does_not_exist || lighting_expired))
4632 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4636 // Create dummy block
4637 block = new MapBlock(this, p, true);
4639 // Add block to sector
4640 sector->insertBlock(block);
4646 //dstream<<"Not found on disk, generating."<<std::endl;
4648 //TimeTaker("emergeBlock() generate");
4650 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4653 If the block doesn't exist, generate the block.
4657 block = generateBlock(p, block, sector, changed_blocks,
4658 lighting_invalidated_blocks);
4661 if(lighting_expired)
4663 lighting_invalidated_blocks.insert(p, block);
4667 Initially update sunlight
4671 core::map<v3s16, bool> light_sources;
4672 bool black_air_left = false;
4673 bool bottom_invalid =
4674 block->propagateSunlight(light_sources, true,
4675 &black_air_left, true);
4677 // If sunlight didn't reach everywhere and part of block is
4678 // above ground, lighting has to be properly updated
4679 //if(black_air_left && some_part_underground)
4682 lighting_invalidated_blocks[block->getPos()] = block;
4687 lighting_invalidated_blocks[block->getPos()] = block;
4694 s16 ServerMap::findGroundLevel(v2s16 p2d)
4697 Uh, just do something random...
4699 // Find existing map from top to down
4702 v3s16 p(p2d.X, max, p2d.Y);
4703 for(; p.Y>min; p.Y--)
4705 MapNode n = getNodeNoEx(p);
4706 if(n.d != CONTENT_IGNORE)
4711 // If this node is not air, go to plan b
4712 if(getNodeNoEx(p).d != CONTENT_AIR)
4714 // Search existing walkable and return it
4715 for(; p.Y>min; p.Y--)
4717 MapNode n = getNodeNoEx(p);
4718 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4724 Plan B: Get from map generator perlin noise function
4726 // This won't work if proper generation is disabled
4727 if(m_chunksize == 0)
4728 return WATER_LEVEL+2;
4729 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4733 void ServerMap::createDir(std::string path)
4735 if(fs::CreateDir(path) == false)
4737 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4738 <<"\""<<path<<"\""<<std::endl;
4739 throw BaseException("ServerMap failed to create directory");
4743 std::string ServerMap::getSectorSubDir(v2s16 pos)
4746 snprintf(cc, 9, "%.4x%.4x",
4747 (unsigned int)pos.X&0xffff,
4748 (unsigned int)pos.Y&0xffff);
4750 return std::string(cc);
4753 std::string ServerMap::getSectorDir(v2s16 pos)
4755 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4758 v2s16 ServerMap::getSectorPos(std::string dirname)
4760 if(dirname.size() != 8)
4761 throw InvalidFilenameException("Invalid sector directory name");
4763 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4765 throw InvalidFilenameException("Invalid sector directory name");
4766 v2s16 pos((s16)x, (s16)y);
4770 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4772 v2s16 p2d = getSectorPos(sectordir);
4774 if(blockfile.size() != 4){
4775 throw InvalidFilenameException("Invalid block filename");
4778 int r = sscanf(blockfile.c_str(), "%4x", &y);
4780 throw InvalidFilenameException("Invalid block filename");
4781 return v3s16(p2d.X, y, p2d.Y);
4784 void ServerMap::save(bool only_changed)
4786 DSTACK(__FUNCTION_NAME);
4787 if(m_map_saving_enabled == false)
4789 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4793 if(only_changed == false)
4794 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4799 // Disable saving chunk metadata file if chunks are disabled
4800 if(m_chunksize != 0)
4805 u32 sector_meta_count = 0;
4806 u32 block_count = 0;
4809 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4811 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4812 for(; i.atEnd() == false; i++)
4814 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4815 assert(sector->getId() == MAPSECTOR_SERVER);
4817 if(sector->differs_from_disk || only_changed == false)
4819 saveSectorMeta(sector);
4820 sector_meta_count++;
4822 core::list<MapBlock*> blocks;
4823 sector->getBlocks(blocks);
4824 core::list<MapBlock*>::Iterator j;
4825 for(j=blocks.begin(); j!=blocks.end(); j++)
4827 MapBlock *block = *j;
4828 if(block->getChangedFlag() || only_changed == false)
4833 /*dstream<<"ServerMap: Written block ("
4834 <<block->getPos().X<<","
4835 <<block->getPos().Y<<","
4836 <<block->getPos().Z<<")"
4845 Only print if something happened or saved whole map
4847 if(only_changed == false || sector_meta_count != 0
4848 || block_count != 0)
4850 dstream<<DTIME<<"ServerMap: Written: "
4851 <<sector_meta_count<<" sector metadata files, "
4852 <<block_count<<" block files"
4858 // NOTE: Doing this is insane. Deprecated and probably broken.
4859 void ServerMap::loadAll()
4861 DSTACK(__FUNCTION_NAME);
4862 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4867 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4869 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4871 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4874 s32 printed_counter = -100000;
4875 s32 count = list.size();
4877 std::vector<fs::DirListNode>::iterator i;
4878 for(i=list.begin(); i!=list.end(); i++)
4880 if(counter > printed_counter + 10)
4882 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4883 printed_counter = counter;
4887 MapSector *sector = NULL;
4889 // We want directories
4893 sector = loadSectorMeta(i->name);
4895 catch(InvalidFilenameException &e)
4897 // This catches unknown crap in directory
4900 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4901 (m_savedir+"/sectors/"+i->name);
4902 std::vector<fs::DirListNode>::iterator i2;
4903 for(i2=list2.begin(); i2!=list2.end(); i2++)
4909 loadBlock(i->name, i2->name, sector);
4911 catch(InvalidFilenameException &e)
4913 // This catches unknown crap in directory
4917 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4922 void ServerMap::saveMasterHeightmap()
4924 DSTACK(__FUNCTION_NAME);
4926 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4928 createDir(m_savedir);
4930 /*std::string fullpath = m_savedir + "/master_heightmap";
4931 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4932 if(o.good() == false)
4933 throw FileNotGoodException("Cannot open master heightmap");*/
4935 // Format used for writing
4936 //u8 version = SER_FMT_VER_HIGHEST;
4939 void ServerMap::loadMasterHeightmap()
4941 DSTACK(__FUNCTION_NAME);
4943 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4945 /*std::string fullpath = m_savedir + "/master_heightmap";
4946 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4947 if(is.good() == false)
4948 throw FileNotGoodException("Cannot open master heightmap");*/
4952 void ServerMap::saveMapMeta()
4954 DSTACK(__FUNCTION_NAME);
4956 dstream<<"INFO: ServerMap::saveMapMeta(): "
4957 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4960 createDir(m_savedir);
4962 std::string fullpath = m_savedir + "/map_meta.txt";
4963 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4964 if(os.good() == false)
4966 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4967 <<"could not open"<<fullpath<<std::endl;
4968 throw FileNotGoodException("Cannot open chunk metadata");
4972 params.setU64("seed", m_seed);
4973 params.setS32("chunksize", m_chunksize);
4975 params.writeLines(os);
4977 os<<"[end_of_params]\n";
4981 void ServerMap::loadMapMeta()
4983 DSTACK(__FUNCTION_NAME);
4985 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
4988 std::string fullpath = m_savedir + "/map_meta.txt";
4989 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4990 if(is.good() == false)
4992 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4993 <<"could not open"<<fullpath<<std::endl;
4994 throw FileNotGoodException("Cannot open map metadata");
5002 throw SerializationError
5003 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5005 std::getline(is, line);
5006 std::string trimmedline = trim(line);
5007 if(trimmedline == "[end_of_params]")
5009 params.parseConfigLine(line);
5012 m_seed = params.getU64("seed");
5013 m_chunksize = params.getS32("chunksize");
5015 dstream<<"INFO: ServerMap::loadMapMeta(): "
5016 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5020 void ServerMap::saveChunkMeta()
5022 DSTACK(__FUNCTION_NAME);
5024 // This should not be called if chunks are disabled.
5025 assert(m_chunksize != 0);
5027 u32 count = m_chunks.size();
5029 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5030 <<count<<" chunks"<<std::endl;
5032 createDir(m_savedir);
5034 std::string fullpath = m_savedir + "/chunk_meta";
5035 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5036 if(os.good() == false)
5038 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5039 <<"could not open"<<fullpath<<std::endl;
5040 throw FileNotGoodException("Cannot open chunk metadata");
5046 os.write((char*)&version, 1);
5051 writeU32(buf, count);
5052 os.write((char*)buf, 4);
5054 for(core::map<v2s16, MapChunk*>::Iterator
5055 i = m_chunks.getIterator();
5056 i.atEnd()==false; i++)
5058 v2s16 p = i.getNode()->getKey();
5059 MapChunk *chunk = i.getNode()->getValue();
5062 os.write((char*)buf, 4);
5064 chunk->serialize(os, version);
5068 void ServerMap::loadChunkMeta()
5070 DSTACK(__FUNCTION_NAME);
5072 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5075 std::string fullpath = m_savedir + "/chunk_meta";
5076 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5077 if(is.good() == false)
5079 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5080 <<"could not open"<<fullpath<<std::endl;
5081 throw FileNotGoodException("Cannot open chunk metadata");
5087 is.read((char*)&version, 1);
5092 is.read((char*)buf, 4);
5093 u32 count = readU32(buf);
5095 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5096 <<count<<" chunks"<<std::endl;
5098 for(u32 i=0; i<count; i++)
5101 MapChunk *chunk = new MapChunk();
5103 is.read((char*)buf, 4);
5106 chunk->deSerialize(is, version);
5107 m_chunks.insert(p, chunk);
5111 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5113 DSTACK(__FUNCTION_NAME);
5114 // Format used for writing
5115 u8 version = SER_FMT_VER_HIGHEST;
5117 v2s16 pos = sector->getPos();
5118 createDir(m_savedir);
5119 createDir(m_savedir+"/sectors");
5120 std::string dir = getSectorDir(pos);
5123 std::string fullpath = dir + "/meta";
5124 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5125 if(o.good() == false)
5126 throw FileNotGoodException("Cannot open sector metafile");
5128 sector->serialize(o, version);
5130 sector->differs_from_disk = false;
5133 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5135 DSTACK(__FUNCTION_NAME);
5137 v2s16 p2d = getSectorPos(dirname);
5138 std::string dir = m_savedir + "/sectors/" + dirname;
5140 ServerMapSector *sector = NULL;
5142 std::string fullpath = dir + "/meta";
5143 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5144 if(is.good() == false)
5146 // If the directory exists anyway, it probably is in some old
5147 // format. Just go ahead and create the sector.
5148 if(fs::PathExists(dir))
5150 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5151 <<fullpath<<" doesn't exist but directory does."
5152 <<" Continuing with a sector with no metadata."
5154 sector = new ServerMapSector(this, p2d);
5155 m_sectors.insert(p2d, sector);
5158 throw FileNotGoodException("Cannot open sector metafile");
5162 sector = ServerMapSector::deSerialize
5163 (is, this, p2d, m_sectors);
5166 sector->differs_from_disk = false;
5171 bool ServerMap::loadSectorFull(v2s16 p2d)
5173 DSTACK(__FUNCTION_NAME);
5174 std::string sectorsubdir = getSectorSubDir(p2d);
5176 MapSector *sector = NULL;
5178 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5181 sector = loadSectorMeta(sectorsubdir);
5183 catch(InvalidFilenameException &e)
5187 catch(FileNotGoodException &e)
5191 catch(std::exception &e)
5199 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5200 (m_savedir+"/sectors/"+sectorsubdir);
5201 std::vector<fs::DirListNode>::iterator i2;
5202 for(i2=list2.begin(); i2!=list2.end(); i2++)
5208 loadBlock(sectorsubdir, i2->name, sector);
5210 catch(InvalidFilenameException &e)
5212 // This catches unknown crap in directory
5218 void ServerMap::saveBlock(MapBlock *block)
5220 DSTACK(__FUNCTION_NAME);
5222 Dummy blocks are not written
5224 if(block->isDummy())
5226 /*v3s16 p = block->getPos();
5227 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5228 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5232 // Format used for writing
5233 u8 version = SER_FMT_VER_HIGHEST;
5235 v3s16 p3d = block->getPos();
5236 v2s16 p2d(p3d.X, p3d.Z);
5237 createDir(m_savedir);
5238 createDir(m_savedir+"/sectors");
5239 std::string dir = getSectorDir(p2d);
5242 // Block file is map/sectors/xxxxxxxx/xxxx
5244 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5245 std::string fullpath = dir + "/" + cc;
5246 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5247 if(o.good() == false)
5248 throw FileNotGoodException("Cannot open block data");
5251 [0] u8 serialization version
5254 o.write((char*)&version, 1);
5256 block->serialize(o, version);
5259 Versions up from 9 have block objects.
5263 block->serializeObjects(o, version);
5267 Versions up from 15 have static objects.
5271 block->m_static_objects.serialize(o);
5274 // We just wrote it to the disk
5275 block->resetChangedFlag();
5278 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5280 DSTACK(__FUNCTION_NAME);
5282 // Block file is map/sectors/xxxxxxxx/xxxx
5283 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5286 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5287 if(is.good() == false)
5288 throw FileNotGoodException("Cannot open block file");
5290 v3s16 p3d = getBlockPos(sectordir, blockfile);
5291 v2s16 p2d(p3d.X, p3d.Z);
5293 assert(sector->getPos() == p2d);
5295 u8 version = SER_FMT_VER_INVALID;
5296 is.read((char*)&version, 1);
5299 throw SerializationError("ServerMap::loadBlock(): Failed"
5300 " to read MapBlock version");
5302 /*u32 block_size = MapBlock::serializedLength(version);
5303 SharedBuffer<u8> data(block_size);
5304 is.read((char*)*data, block_size);*/
5306 // This will always return a sector because we're the server
5307 //MapSector *sector = emergeSector(p2d);
5309 MapBlock *block = NULL;
5310 bool created_new = false;
5312 block = sector->getBlockNoCreate(p3d.Y);
5314 catch(InvalidPositionException &e)
5316 block = sector->createBlankBlockNoInsert(p3d.Y);
5320 // deserialize block data
5321 block->deSerialize(is, version);
5324 Versions up from 9 have block objects.
5328 block->updateObjects(is, version, NULL, 0);
5332 Versions up from 15 have static objects.
5336 block->m_static_objects.deSerialize(is);
5340 sector->insertBlock(block);
5343 Convert old formats to new and save
5346 // Save old format blocks in new format
5347 if(version < SER_FMT_VER_HIGHEST)
5352 // We just loaded it from the disk, so it's up-to-date.
5353 block->resetChangedFlag();
5356 catch(SerializationError &e)
5358 dstream<<"WARNING: Invalid block data on disk "
5359 "(SerializationError). Ignoring. "
5360 "A new one will be generated."
5363 // TODO: Backup file; name is in fullpath.
5367 void ServerMap::PrintInfo(std::ostream &out)
5378 ClientMap::ClientMap(
5380 MapDrawControl &control,
5381 scene::ISceneNode* parent,
5382 scene::ISceneManager* mgr,
5386 scene::ISceneNode(parent, mgr, id),
5389 m_camera_position(0,0,0),
5390 m_camera_direction(0,0,1)
5392 m_camera_mutex.Init();
5393 assert(m_camera_mutex.IsInitialized());
5395 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5396 BS*1000000,BS*1000000,BS*1000000);
5399 ClientMap::~ClientMap()
5401 /*JMutexAutoLock lock(mesh_mutex);
5410 MapSector * ClientMap::emergeSector(v2s16 p2d)
5412 DSTACK(__FUNCTION_NAME);
5413 // Check that it doesn't exist already
5415 return getSectorNoGenerate(p2d);
5417 catch(InvalidPositionException &e)
5422 ClientMapSector *sector = new ClientMapSector(this, p2d);
5425 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5426 m_sectors.insert(p2d, sector);
5432 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5434 DSTACK(__FUNCTION_NAME);
5435 ClientMapSector *sector = NULL;
5437 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5439 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5443 sector = (ClientMapSector*)n->getValue();
5444 assert(sector->getId() == MAPSECTOR_CLIENT);
5448 sector = new ClientMapSector(this, p2d);
5450 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5451 m_sectors.insert(p2d, sector);
5455 sector->deSerialize(is);
5458 void ClientMap::OnRegisterSceneNode()
5462 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5463 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5466 ISceneNode::OnRegisterSceneNode();
5469 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5471 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5472 DSTACK(__FUNCTION_NAME);
5474 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5477 Get time for measuring timeout.
5479 Measuring time is very useful for long delays when the
5480 machine is swapping a lot.
5482 int time1 = time(0);
5484 //u32 daynight_ratio = m_client->getDayNightRatio();
5486 m_camera_mutex.Lock();
5487 v3f camera_position = m_camera_position;
5488 v3f camera_direction = m_camera_direction;
5489 m_camera_mutex.Unlock();
5492 Get all blocks and draw all visible ones
5495 v3s16 cam_pos_nodes(
5496 camera_position.X / BS,
5497 camera_position.Y / BS,
5498 camera_position.Z / BS);
5500 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5502 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5503 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5505 // Take a fair amount as we will be dropping more out later
5507 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5508 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5509 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5511 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5512 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5513 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5515 u32 vertex_count = 0;
5517 // For limiting number of mesh updates per frame
5518 u32 mesh_update_count = 0;
5520 u32 blocks_would_have_drawn = 0;
5521 u32 blocks_drawn = 0;
5523 //NOTE: The sectors map should be locked but we're not doing it
5524 // because it'd cause too much delays
5526 int timecheck_counter = 0;
5527 core::map<v2s16, MapSector*>::Iterator si;
5528 si = m_sectors.getIterator();
5529 for(; si.atEnd() == false; si++)
5532 timecheck_counter++;
5533 if(timecheck_counter > 50)
5535 timecheck_counter = 0;
5536 int time2 = time(0);
5537 if(time2 > time1 + 4)
5539 dstream<<"ClientMap::renderMap(): "
5540 "Rendering takes ages, returning."
5547 MapSector *sector = si.getNode()->getValue();
5548 v2s16 sp = sector->getPos();
5550 if(m_control.range_all == false)
5552 if(sp.X < p_blocks_min.X
5553 || sp.X > p_blocks_max.X
5554 || sp.Y < p_blocks_min.Z
5555 || sp.Y > p_blocks_max.Z)
5559 core::list< MapBlock * > sectorblocks;
5560 sector->getBlocks(sectorblocks);
5566 core::list< MapBlock * >::Iterator i;
5567 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5569 MapBlock *block = *i;
5572 Compare block position to camera position, skip
5573 if not seen on display
5576 float range = 100000 * BS;
5577 if(m_control.range_all == false)
5578 range = m_control.wanted_range * BS;
5581 if(isBlockInSight(block->getPos(), camera_position,
5582 camera_direction, range, &d) == false)
5587 // This is ugly (spherical distance limit?)
5588 /*if(m_control.range_all == false &&
5589 d - 0.5*BS*MAP_BLOCKSIZE > range)
5594 Update expired mesh (used for day/night change)
5596 It doesn't work exactly like it should now with the
5597 tasked mesh update but whatever.
5600 bool mesh_expired = false;
5603 JMutexAutoLock lock(block->mesh_mutex);
5605 mesh_expired = block->getMeshExpired();
5607 // Mesh has not been expired and there is no mesh:
5608 // block has no content
5609 if(block->mesh == NULL && mesh_expired == false)
5613 f32 faraway = BS*50;
5614 //f32 faraway = m_control.wanted_range * BS;
5617 This has to be done with the mesh_mutex unlocked
5619 // Pretty random but this should work somewhat nicely
5620 if(mesh_expired && (
5621 (mesh_update_count < 3
5622 && (d < faraway || mesh_update_count < 2)
5625 (m_control.range_all && mesh_update_count < 20)
5628 /*if(mesh_expired && mesh_update_count < 6
5629 && (d < faraway || mesh_update_count < 3))*/
5631 mesh_update_count++;
5633 // Mesh has been expired: generate new mesh
5634 //block->updateMesh(daynight_ratio);
5635 m_client->addUpdateMeshTask(block->getPos());
5637 mesh_expired = false;
5642 Draw the faces of the block
5645 JMutexAutoLock lock(block->mesh_mutex);
5647 scene::SMesh *mesh = block->mesh;
5652 blocks_would_have_drawn++;
5653 if(blocks_drawn >= m_control.wanted_max_blocks
5654 && m_control.range_all == false
5655 && d > m_control.wanted_min_range * BS)
5659 u32 c = mesh->getMeshBufferCount();
5661 for(u32 i=0; i<c; i++)
5663 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5664 const video::SMaterial& material = buf->getMaterial();
5665 video::IMaterialRenderer* rnd =
5666 driver->getMaterialRenderer(material.MaterialType);
5667 bool transparent = (rnd && rnd->isTransparent());
5668 // Render transparent on transparent pass and likewise.
5669 if(transparent == is_transparent_pass)
5672 This *shouldn't* hurt too much because Irrlicht
5673 doesn't change opengl textures if the old
5674 material is set again.
5676 driver->setMaterial(buf->getMaterial());
5677 driver->drawMeshBuffer(buf);
5678 vertex_count += buf->getVertexCount();
5682 } // foreach sectorblocks
5685 m_control.blocks_drawn = blocks_drawn;
5686 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5688 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5689 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5692 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5693 core::map<v3s16, MapBlock*> *affected_blocks)
5695 bool changed = false;
5697 Add it to all blocks touching it
5700 v3s16(0,0,0), // this
5701 v3s16(0,0,1), // back
5702 v3s16(0,1,0), // top
5703 v3s16(1,0,0), // right
5704 v3s16(0,0,-1), // front
5705 v3s16(0,-1,0), // bottom
5706 v3s16(-1,0,0), // left
5708 for(u16 i=0; i<7; i++)
5710 v3s16 p2 = p + dirs[i];
5711 // Block position of neighbor (or requested) node
5712 v3s16 blockpos = getNodeBlockPos(p2);
5713 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5714 if(blockref == NULL)
5716 // Relative position of requested node
5717 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5718 if(blockref->setTempMod(relpos, mod))
5723 if(changed && affected_blocks!=NULL)
5725 for(u16 i=0; i<7; i++)
5727 v3s16 p2 = p + dirs[i];
5728 // Block position of neighbor (or requested) node
5729 v3s16 blockpos = getNodeBlockPos(p2);
5730 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5731 if(blockref == NULL)
5733 affected_blocks->insert(blockpos, blockref);
5739 bool ClientMap::clearTempMod(v3s16 p,
5740 core::map<v3s16, MapBlock*> *affected_blocks)
5742 bool changed = false;
5744 v3s16(0,0,0), // this
5745 v3s16(0,0,1), // back
5746 v3s16(0,1,0), // top
5747 v3s16(1,0,0), // right
5748 v3s16(0,0,-1), // front
5749 v3s16(0,-1,0), // bottom
5750 v3s16(-1,0,0), // left
5752 for(u16 i=0; i<7; i++)
5754 v3s16 p2 = p + dirs[i];
5755 // Block position of neighbor (or requested) node
5756 v3s16 blockpos = getNodeBlockPos(p2);
5757 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5758 if(blockref == NULL)
5760 // Relative position of requested node
5761 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5762 if(blockref->clearTempMod(relpos))
5767 if(changed && affected_blocks!=NULL)
5769 for(u16 i=0; i<7; i++)
5771 v3s16 p2 = p + dirs[i];
5772 // Block position of neighbor (or requested) node
5773 v3s16 blockpos = getNodeBlockPos(p2);
5774 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5775 if(blockref == NULL)
5777 affected_blocks->insert(blockpos, blockref);
5783 void ClientMap::expireMeshes(bool only_daynight_diffed)
5785 TimeTaker timer("expireMeshes()");
5787 core::map<v2s16, MapSector*>::Iterator si;
5788 si = m_sectors.getIterator();
5789 for(; si.atEnd() == false; si++)
5791 MapSector *sector = si.getNode()->getValue();
5793 core::list< MapBlock * > sectorblocks;
5794 sector->getBlocks(sectorblocks);
5796 core::list< MapBlock * >::Iterator i;
5797 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5799 MapBlock *block = *i;
5801 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5807 JMutexAutoLock lock(block->mesh_mutex);
5808 if(block->mesh != NULL)
5810 /*block->mesh->drop();
5811 block->mesh = NULL;*/
5812 block->setMeshExpired(true);
5819 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5821 assert(mapType() == MAPTYPE_CLIENT);
5824 v3s16 p = blockpos + v3s16(0,0,0);
5825 MapBlock *b = getBlockNoCreate(p);
5826 b->updateMesh(daynight_ratio);
5827 //b->setMeshExpired(true);
5829 catch(InvalidPositionException &e){}
5832 v3s16 p = blockpos + v3s16(-1,0,0);
5833 MapBlock *b = getBlockNoCreate(p);
5834 b->updateMesh(daynight_ratio);
5835 //b->setMeshExpired(true);
5837 catch(InvalidPositionException &e){}
5839 v3s16 p = blockpos + v3s16(0,-1,0);
5840 MapBlock *b = getBlockNoCreate(p);
5841 b->updateMesh(daynight_ratio);
5842 //b->setMeshExpired(true);
5844 catch(InvalidPositionException &e){}
5846 v3s16 p = blockpos + v3s16(0,0,-1);
5847 MapBlock *b = getBlockNoCreate(p);
5848 b->updateMesh(daynight_ratio);
5849 //b->setMeshExpired(true);
5851 catch(InvalidPositionException &e){}
5856 Update mesh of block in which the node is, and if the node is at the
5857 leading edge, update the appropriate leading blocks too.
5859 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5867 v3s16 blockposes[4];
5868 for(u32 i=0; i<4; i++)
5870 v3s16 np = nodepos + dirs[i];
5871 blockposes[i] = getNodeBlockPos(np);
5872 // Don't update mesh of block if it has been done already
5873 bool already_updated = false;
5874 for(u32 j=0; j<i; j++)
5876 if(blockposes[j] == blockposes[i])
5878 already_updated = true;
5885 MapBlock *b = getBlockNoCreate(blockposes[i]);
5886 b->updateMesh(daynight_ratio);
5891 void ClientMap::PrintInfo(std::ostream &out)
5902 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5907 MapVoxelManipulator::~MapVoxelManipulator()
5909 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5913 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5915 TimeTaker timer1("emerge", &emerge_time);
5917 // Units of these are MapBlocks
5918 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5919 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5921 VoxelArea block_area_nodes
5922 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5924 addArea(block_area_nodes);
5926 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5927 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5928 for(s32 x=p_min.X; x<=p_max.X; x++)
5931 core::map<v3s16, bool>::Node *n;
5932 n = m_loaded_blocks.find(p);
5936 bool block_data_inexistent = false;
5939 TimeTaker timer1("emerge load", &emerge_load_time);
5941 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5942 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5945 dstream<<std::endl;*/
5947 MapBlock *block = m_map->getBlockNoCreate(p);
5948 if(block->isDummy())
5949 block_data_inexistent = true;
5951 block->copyTo(*this);
5953 catch(InvalidPositionException &e)
5955 block_data_inexistent = true;
5958 if(block_data_inexistent)
5960 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5961 // Fill with VOXELFLAG_INEXISTENT
5962 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5963 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5965 s32 i = m_area.index(a.MinEdge.X,y,z);
5966 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5970 m_loaded_blocks.insert(p, !block_data_inexistent);
5973 //dstream<<"emerge done"<<std::endl;
5977 SUGG: Add an option to only update eg. water and air nodes.
5978 This will make it interfere less with important stuff if
5981 void MapVoxelManipulator::blitBack
5982 (core::map<v3s16, MapBlock*> & modified_blocks)
5984 if(m_area.getExtent() == v3s16(0,0,0))
5987 //TimeTaker timer1("blitBack");
5989 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5990 <<m_loaded_blocks.size()<<std::endl;*/
5993 Initialize block cache
5995 v3s16 blockpos_last;
5996 MapBlock *block = NULL;
5997 bool block_checked_in_modified = false;
5999 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6000 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6001 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6005 u8 f = m_flags[m_area.index(p)];
6006 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6009 MapNode &n = m_data[m_area.index(p)];
6011 v3s16 blockpos = getNodeBlockPos(p);
6016 if(block == NULL || blockpos != blockpos_last){
6017 block = m_map->getBlockNoCreate(blockpos);
6018 blockpos_last = blockpos;
6019 block_checked_in_modified = false;
6022 // Calculate relative position in block
6023 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6025 // Don't continue if nothing has changed here
6026 if(block->getNode(relpos) == n)
6029 //m_map->setNode(m_area.MinEdge + p, n);
6030 block->setNode(relpos, n);
6033 Make sure block is in modified_blocks
6035 if(block_checked_in_modified == false)
6037 modified_blocks[blockpos] = block;
6038 block_checked_in_modified = true;
6041 catch(InvalidPositionException &e)
6047 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6048 MapVoxelManipulator(map),
6049 m_create_area(false)
6053 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6057 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6059 // Just create the area so that it can be pointed to
6060 VoxelManipulator::emerge(a, caller_id);
6063 void ManualMapVoxelManipulator::initialEmerge(
6064 v3s16 blockpos_min, v3s16 blockpos_max)
6066 TimeTaker timer1("initialEmerge", &emerge_time);
6068 // Units of these are MapBlocks
6069 v3s16 p_min = blockpos_min;
6070 v3s16 p_max = blockpos_max;
6072 VoxelArea block_area_nodes
6073 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6075 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6078 dstream<<"initialEmerge: area: ";
6079 block_area_nodes.print(dstream);
6080 dstream<<" ("<<size_MB<<"MB)";
6084 addArea(block_area_nodes);
6086 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6087 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6088 for(s32 x=p_min.X; x<=p_max.X; x++)
6091 core::map<v3s16, bool>::Node *n;
6092 n = m_loaded_blocks.find(p);
6096 bool block_data_inexistent = false;
6099 TimeTaker timer1("emerge load", &emerge_load_time);
6101 MapBlock *block = m_map->getBlockNoCreate(p);
6102 if(block->isDummy())
6103 block_data_inexistent = true;
6105 block->copyTo(*this);
6107 catch(InvalidPositionException &e)
6109 block_data_inexistent = true;
6112 if(block_data_inexistent)
6115 Mark area inexistent
6117 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6118 // Fill with VOXELFLAG_INEXISTENT
6119 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6120 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6122 s32 i = m_area.index(a.MinEdge.X,y,z);
6123 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6127 m_loaded_blocks.insert(p, !block_data_inexistent);
6131 void ManualMapVoxelManipulator::blitBackAll(
6132 core::map<v3s16, MapBlock*> * modified_blocks)
6134 if(m_area.getExtent() == v3s16(0,0,0))
6138 Copy data of all blocks
6140 for(core::map<v3s16, bool>::Iterator
6141 i = m_loaded_blocks.getIterator();
6142 i.atEnd() == false; i++)
6144 bool existed = i.getNode()->getValue();
6145 if(existed == false)
6147 v3s16 p = i.getNode()->getKey();
6148 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6151 dstream<<"WARNING: "<<__FUNCTION_NAME
6152 <<": got NULL block "
6153 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6158 block->copyFrom(*this);
6161 modified_blocks->insert(p, block);