3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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();
842 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
843 core::map<v3s16, MapBlock*> &modified_blocks)
846 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
847 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
850 From this node to nodes underneath:
851 If lighting is sunlight (1.0), unlight neighbours and
856 v3s16 toppos = p + v3s16(0,1,0);
857 v3s16 bottompos = p + v3s16(0,-1,0);
859 bool node_under_sunlight = true;
860 core::map<v3s16, bool> light_sources;
863 If there is a node at top and it doesn't have sunlight,
864 there has not been any sunlight going down.
866 Otherwise there probably is.
869 MapNode topnode = getNode(toppos);
871 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
872 node_under_sunlight = false;
874 catch(InvalidPositionException &e)
880 If the new node is solid and there is grass below, change it to mud
882 if(content_features(n.d).walkable == true)
885 MapNode bottomnode = getNode(bottompos);
887 if(bottomnode.d == CONTENT_GRASS
888 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
890 bottomnode.d = CONTENT_MUD;
891 setNode(bottompos, bottomnode);
894 catch(InvalidPositionException &e)
902 If the new node is mud and it is under sunlight, change it
905 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):
1796 m_map_metadata_changed(true)
1798 dstream<<__FUNCTION_NAME<<std::endl;
1801 //m_chunksize = 16; // Too slow
1802 m_chunksize = 8; // Takes a few seconds
1806 m_seed = (((u64)(myrand()%0xffff)<<0)
1807 + ((u64)(myrand()%0xffff)<<16)
1808 + ((u64)(myrand()%0xffff)<<32)
1809 + ((u64)(myrand()%0xffff)<<48));
1812 Experimental and debug stuff
1819 Try to load map; if not found, create a new one.
1822 m_savedir = savedir;
1823 m_map_saving_enabled = false;
1827 // If directory exists, check contents and load if possible
1828 if(fs::PathExists(m_savedir))
1830 // If directory is empty, it is safe to save into it.
1831 if(fs::GetDirListing(m_savedir).size() == 0)
1833 dstream<<DTIME<<"Server: Empty save directory is valid."
1835 m_map_saving_enabled = true;
1840 // Load map metadata (seed, chunksize)
1843 // Load chunk metadata
1846 catch(FileNotGoodException &e){
1847 dstream<<DTIME<<"WARNING: Server: Could not load "
1848 <<"metafile(s). Disabling chunk-based "
1849 <<"generation."<<std::endl;
1853 /*// Load sector (0,0) and throw and exception on fail
1854 if(loadSectorFull(v2s16(0,0)) == false)
1855 throw LoadError("Failed to load sector (0,0)");*/
1857 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1858 "metadata and sector (0,0) from "<<savedir<<
1859 ", assuming valid save directory."
1862 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1863 <<"and chunk metadata from "<<savedir
1864 <<", assuming valid save directory."
1867 m_map_saving_enabled = true;
1868 // Map loaded, not creating new one
1872 // If directory doesn't exist, it is safe to save to it
1874 m_map_saving_enabled = true;
1877 catch(std::exception &e)
1879 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1880 <<", exception: "<<e.what()<<std::endl;
1881 dstream<<"Please remove the map or fix it."<<std::endl;
1882 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1885 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1887 // Create zero sector
1888 emergeSector(v2s16(0,0));
1890 // Initially write whole map
1894 ServerMap::~ServerMap()
1896 dstream<<__FUNCTION_NAME<<std::endl;
1900 if(m_map_saving_enabled)
1903 // Save only changed parts
1905 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1909 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1912 catch(std::exception &e)
1914 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1915 <<", exception: "<<e.what()<<std::endl;
1921 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1922 for(; i.atEnd() == false; i++)
1924 MapChunk *chunk = i.getNode()->getValue();
1930 Some helper functions for the map generator
1933 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1935 v3s16 em = vmanip.m_area.getExtent();
1936 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1937 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1938 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1940 for(y=y_nodes_max; y>=y_nodes_min; y--)
1942 MapNode &n = vmanip.m_data[i];
1943 if(content_walkable(n.d))
1946 vmanip.m_area.add_y(em, i, -1);
1948 if(y >= y_nodes_min)
1954 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1956 v3s16 em = vmanip.m_area.getExtent();
1957 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1958 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1959 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1961 for(y=y_nodes_max; y>=y_nodes_min; y--)
1963 MapNode &n = vmanip.m_data[i];
1964 if(content_walkable(n.d)
1965 && n.d != CONTENT_TREE
1966 && n.d != CONTENT_LEAVES)
1969 vmanip.m_area.add_y(em, i, -1);
1971 if(y >= y_nodes_min)
1977 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1979 MapNode treenode(CONTENT_TREE);
1980 MapNode leavesnode(CONTENT_LEAVES);
1982 s16 trunk_h = myrand_range(3, 6);
1984 for(s16 ii=0; ii<trunk_h; ii++)
1986 if(vmanip.m_area.contains(p1))
1987 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1991 // p1 is now the last piece of the trunk
1994 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1995 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1996 Buffer<u8> leaves_d(leaves_a.getVolume());
1997 for(s32 i=0; i<leaves_a.getVolume(); i++)
2000 // Force leaves at near the end of the trunk
2003 for(s16 z=-d; z<=d; z++)
2004 for(s16 y=-d; y<=d; y++)
2005 for(s16 x=-d; x<=d; x++)
2007 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2011 // Add leaves randomly
2012 for(u32 iii=0; iii<7; iii++)
2017 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2018 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2019 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2022 for(s16 z=0; z<=d; z++)
2023 for(s16 y=0; y<=d; y++)
2024 for(s16 x=0; x<=d; x++)
2026 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2030 // Blit leaves to vmanip
2031 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2032 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2033 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2037 if(vmanip.m_area.contains(p) == false)
2039 u32 vi = vmanip.m_area.index(p);
2040 if(vmanip.m_data[vi].d != CONTENT_AIR)
2042 u32 i = leaves_a.index(x,y,z);
2043 if(leaves_d[i] == 1)
2044 vmanip.m_data[vi] = leavesnode;
2049 Noise functions. Make sure seed is mangled differently in each one.
2052 // Amount of trees per area in nodes
2053 double tree_amount_2d(u64 seed, v2s16 p)
2055 double noise = noise2d_perlin(
2056 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2058 double zeroval = -0.3;
2062 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2065 #define AVERAGE_MUD_AMOUNT 4
2067 double base_rock_level_2d(u64 seed, v2s16 p)
2069 // The base ground level
2070 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2071 + 20. * noise2d_perlin(
2072 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2073 (seed>>32)+654879876, 6, 0.6);
2075 /*// A bit hillier one
2076 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2077 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2078 (seed>>27)+90340, 6, 0.69);
2082 // Higher ground level
2083 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2084 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2085 seed+85039, 5, 0.69);
2086 //higher = 30; // For debugging
2088 // Limit higher to at least base
2092 // Steepness factor of cliffs
2093 double b = 1.0 + 1.0 * noise2d_perlin(
2094 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2096 b = rangelim(b, 0.0, 1000.0);
2099 b = rangelim(b, 3.0, 1000.0);
2100 //dstream<<"b="<<b<<std::endl;
2103 // Offset to more low
2104 double a_off = -0.2;
2105 // High/low selector
2106 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2107 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2108 seed-359, 6, 0.7));*/
2109 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2110 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2111 seed-359, 5, 0.60));
2113 a = rangelim(a, 0.0, 1.0);
2115 //dstream<<"a="<<a<<std::endl;
2117 double h = base*(1.0-a) + higher*a;
2124 double get_mud_add_amount(u64 seed, v2s16 p)
2126 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2127 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2128 seed+91013, 3, 0.55));
2132 Adds random objects to block, depending on the content of the block
2134 void addRandomObjects(MapBlock *block)
2136 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2137 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2139 bool last_node_walkable = false;
2140 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2143 MapNode n = block->getNodeNoEx(p);
2144 if(n.d == CONTENT_IGNORE)
2146 if(content_features(n.d).liquid_type != LIQUID_NONE)
2148 if(content_features(n.d).walkable)
2150 last_node_walkable = true;
2153 if(last_node_walkable)
2155 // If block contains light information
2156 if(content_features(n.d).param_type == CPT_LIGHT)
2158 if(n.getLight(LIGHTBANK_DAY) <= 3)
2160 if(myrand() % 300 == 0)
2162 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2164 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2165 std::string data = obj->getStaticData();
2166 StaticObject s_obj(obj->getType(),
2167 obj->getBasePosition(), data);
2169 block->m_static_objects.insert(0, s_obj);
2170 block->m_static_objects.insert(0, s_obj);
2171 block->m_static_objects.insert(0, s_obj);
2172 block->m_static_objects.insert(0, s_obj);
2173 block->m_static_objects.insert(0, s_obj);
2174 block->m_static_objects.insert(0, s_obj);
2177 if(myrand() % 300 == 0)
2179 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2181 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2182 std::string data = obj->getStaticData();
2183 StaticObject s_obj(obj->getType(),
2184 obj->getBasePosition(), data);
2186 block->m_static_objects.insert(0, s_obj);
2192 last_node_walkable = false;
2195 block->setChangedFlag();
2198 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2201 This is the main map generation method
2204 void makeChunk(ChunkMakeData *data)
2209 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2210 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2211 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2212 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2213 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2214 *(u32)h_blocks*MAP_BLOCKSIZE;
2215 v3s16 bigarea_blocks_min(
2216 data->sectorpos_bigbase.X,
2218 data->sectorpos_bigbase.Y
2220 v3s16 bigarea_blocks_max(
2221 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2223 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2225 s16 lighting_min_d = 0-data->max_spread_amount;
2226 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2227 + data->max_spread_amount-1;
2230 data->vmanip.clearFlag(0xff);
2232 TimeTaker timer_generate("makeChunk() generate");
2234 // Maximum height of the stone surface and obstacles.
2235 // This is used to disable cave generation from going too high.
2236 s16 stone_surface_max_y = 0;
2239 Generate general ground level to full area
2243 TimeTaker timer1("Generating ground level");
2246 NoiseBuffer noisebuf1;
2247 //NoiseBuffer noisebuf2;
2250 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2252 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2254 v3f maxpos_f = minpos_f + v3f(
2255 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2256 y_nodes_max-y_nodes_min,
2257 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2259 v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2261 TimeTaker timer("noisebuf.create");
2263 noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
2264 minpos_f.X, minpos_f.Y, minpos_f.Z,
2265 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2266 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2267 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
2268 minpos_f.X, minpos_f.Y, minpos_f.Z,
2269 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2270 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2271 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
2272 minpos_f.X, minpos_f.Y, minpos_f.Z,
2273 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2274 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2277 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2278 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2281 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2283 // Ground height at this point
2284 float surface_y_f = 0.0;
2286 // Use perlin noise for ground height
2287 surface_y_f = base_rock_level_2d(data->seed, p2d);
2288 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2290 // Convert to integer
2291 s16 surface_y = (s16)surface_y_f;
2294 if(surface_y > stone_surface_max_y)
2295 stone_surface_max_y = surface_y;
2298 Fill ground with stone
2301 // Use fast index incrementing
2302 v3s16 em = data->vmanip.m_area.getExtent();
2303 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2304 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2306 // Skip if already generated.
2307 // This is done here because there might be a cave at
2308 // any point in ground, which could look like it
2309 // wasn't generated.
2310 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2313 /*s16 noiseval = 50.0 * noise3d_perlin(
2314 0.5+(float)p2d.X/100.0,
2316 0.5+(float)p2d.Y/100.0,
2317 data->seed+123, 5, 0.5);*/
2318 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2319 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2320 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2322 //if(y < surface_y + noiseval)
2325 data->vmanip.m_data[i].d = CONTENT_STONE;
2327 data->vmanip.m_area.add_y(em, i, 1);
2334 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2335 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2338 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2341 Skip of already generated
2344 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2345 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2349 // Ground height at this point
2350 float surface_y_f = 0.0;
2352 // Use perlin noise for ground height
2353 surface_y_f = base_rock_level_2d(data->seed, p2d);
2355 /*// Experimental stuff
2357 float a = highlands_level_2d(data->seed, p2d);
2362 // Convert to integer
2363 s16 surface_y = (s16)surface_y_f;
2366 if(surface_y > stone_surface_max_y)
2367 stone_surface_max_y = surface_y;
2370 Fill ground with stone
2373 // Use fast index incrementing
2374 v3s16 em = data->vmanip.m_area.getExtent();
2375 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2376 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2378 // Skip if already generated.
2379 // This is done here because there might be a cave at
2380 // any point in ground, which could look like it
2381 // wasn't generated.
2382 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2385 data->vmanip.m_data[i].d = CONTENT_STONE;
2387 data->vmanip.m_area.add_y(em, i, 1);
2396 Randomize some parameters
2399 //s32 stone_obstacle_count = 0;
2400 /*s32 stone_obstacle_count =
2401 rangelim((1.0+noise2d(data->seed+897,
2402 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2404 //s16 stone_obstacle_max_height = 0;
2405 /*s16 stone_obstacle_max_height =
2406 rangelim((1.0+noise2d(data->seed+5902,
2407 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2410 Loop this part, it will make stuff look older and newer nicely
2412 const u32 age_loops = 2;
2413 for(u32 i_age=0; i_age<age_loops; i_age++)
2415 /******************************
2416 BEGINNING OF AGING LOOP
2417 ******************************/
2422 //TimeTaker timer1("caves");
2427 u32 caves_count = relative_volume / 400000;
2428 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2429 if(stone_surface_max_y < WATER_LEVEL)
2431 /*u32 caves_count = 0;
2432 u32 bruises_count = 0;*/
2433 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2435 s16 min_tunnel_diameter = 3;
2436 s16 max_tunnel_diameter = 5;
2437 u16 tunnel_routepoints = 20;
2439 v3f main_direction(0,0,0);
2441 bool bruise_surface = (jj > caves_count);
2445 min_tunnel_diameter = 5;
2446 max_tunnel_diameter = myrand_range(10, 20);
2447 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2448 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2450 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2451 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2453 tunnel_routepoints = 5;
2459 // Allowed route area size in nodes
2461 data->sectorpos_base_size*MAP_BLOCKSIZE,
2462 h_blocks*MAP_BLOCKSIZE,
2463 data->sectorpos_base_size*MAP_BLOCKSIZE
2466 // Area starting point in nodes
2468 data->sectorpos_base.X*MAP_BLOCKSIZE,
2469 data->y_blocks_min*MAP_BLOCKSIZE,
2470 data->sectorpos_base.Y*MAP_BLOCKSIZE
2474 //(this should be more than the maximum radius of the tunnel)
2475 //s16 insure = 5; // Didn't work with max_d = 20
2477 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2478 ar += v3s16(1,0,1) * more * 2;
2479 of -= v3s16(1,0,1) * more;
2481 s16 route_y_min = 0;
2482 // Allow half a diameter + 7 over stone surface
2483 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2485 /*// If caves, don't go through surface too often
2486 if(bruise_surface == false)
2487 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2489 // Limit maximum to area
2490 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2494 /*// Minimum is at y=0
2495 route_y_min = -of.Y - 0;*/
2496 // Minimum is at y=max_tunnel_diameter/4
2497 //route_y_min = -of.Y + max_tunnel_diameter/4;
2498 //s16 min = -of.Y + max_tunnel_diameter/4;
2499 s16 min = -of.Y + 0;
2500 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2501 route_y_min = rangelim(route_y_min, 0, route_y_max);
2504 /*dstream<<"route_y_min = "<<route_y_min
2505 <<", route_y_max = "<<route_y_max<<std::endl;*/
2507 s16 route_start_y_min = route_y_min;
2508 s16 route_start_y_max = route_y_max;
2510 // Start every 2nd cave from surface
2511 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2513 if(coming_from_surface)
2515 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2518 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2519 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2521 // Randomize starting position
2523 (float)(myrand()%ar.X)+0.5,
2524 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2525 (float)(myrand()%ar.Z)+0.5
2528 MapNode airnode(CONTENT_AIR);
2531 Generate some tunnel starting from orp
2534 for(u16 j=0; j<tunnel_routepoints; j++)
2536 if(j%7==0 && bruise_surface == false)
2538 main_direction = v3f(
2539 ((float)(myrand()%20)-(float)10)/10,
2540 ((float)(myrand()%20)-(float)10)/30,
2541 ((float)(myrand()%20)-(float)10)/10
2543 main_direction *= (float)myrand_range(1, 3);
2547 s16 min_d = min_tunnel_diameter;
2548 s16 max_d = max_tunnel_diameter;
2549 s16 rs = myrand_range(min_d, max_d);
2554 maxlen = v3s16(rs*7,rs*7,rs*7);
2558 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2563 if(coming_from_surface && j < 3)
2566 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2567 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2568 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2574 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2575 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2576 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2580 vec += main_direction;
2585 else if(rp.X >= ar.X)
2587 if(rp.Y < route_y_min)
2589 else if(rp.Y >= route_y_max)
2590 rp.Y = route_y_max-1;
2593 else if(rp.Z >= ar.Z)
2597 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2599 v3f fp = orp + vec * f;
2600 v3s16 cp(fp.X, fp.Y, fp.Z);
2603 s16 d1 = d0 + rs - 1;
2604 for(s16 z0=d0; z0<=d1; z0++)
2606 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2607 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2608 for(s16 x0=-si; x0<=si-1; x0++)
2610 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2611 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2612 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2613 //s16 si2 = rs - abs(x0);
2614 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2620 /*if(isInArea(p, ar) == false)
2622 // Check only height
2623 if(y < 0 || y >= ar.Y)
2627 //assert(data->vmanip.m_area.contains(p));
2628 if(data->vmanip.m_area.contains(p) == false)
2630 dstream<<"WARNING: "<<__FUNCTION_NAME
2631 <<":"<<__LINE__<<": "
2632 <<"point not in area"
2637 // Just set it to air, it will be changed to
2639 u32 i = data->vmanip.m_area.index(p);
2640 data->vmanip.m_data[i] = airnode;
2642 if(bruise_surface == false)
2645 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2663 //TimeTaker timer1("ore veins");
2668 for(u32 jj=0; jj<relative_volume/1000; jj++)
2670 s16 max_vein_diameter = 3;
2672 // Allowed route area size in nodes
2674 data->sectorpos_base_size*MAP_BLOCKSIZE,
2675 h_blocks*MAP_BLOCKSIZE,
2676 data->sectorpos_base_size*MAP_BLOCKSIZE
2679 // Area starting point in nodes
2681 data->sectorpos_base.X*MAP_BLOCKSIZE,
2682 data->y_blocks_min*MAP_BLOCKSIZE,
2683 data->sectorpos_base.Y*MAP_BLOCKSIZE
2687 //(this should be more than the maximum radius of the tunnel)
2689 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2690 ar += v3s16(1,0,1) * more * 2;
2691 of -= v3s16(1,0,1) * more;
2693 // Randomize starting position
2695 (float)(myrand()%ar.X)+0.5,
2696 (float)(myrand()%ar.Y)+0.5,
2697 (float)(myrand()%ar.Z)+0.5
2700 // Randomize mineral
2703 mineral = MINERAL_COAL;
2705 mineral = MINERAL_IRON;
2708 Generate some vein starting from orp
2711 for(u16 j=0; j<2; j++)
2714 (float)(myrand()%ar.X)+0.5,
2715 (float)(myrand()%ar.Y)+0.5,
2716 (float)(myrand()%ar.Z)+0.5
2718 v3f vec = rp - orp;*/
2720 v3s16 maxlen(5, 5, 5);
2722 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2723 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2724 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2729 else if(rp.X >= ar.X)
2733 else if(rp.Y >= ar.Y)
2737 else if(rp.Z >= ar.Z)
2743 s16 max_d = max_vein_diameter;
2744 s16 rs = myrand_range(min_d, max_d);
2746 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2748 v3f fp = orp + vec * f;
2749 v3s16 cp(fp.X, fp.Y, fp.Z);
2751 s16 d1 = d0 + rs - 1;
2752 for(s16 z0=d0; z0<=d1; z0++)
2754 s16 si = rs - abs(z0);
2755 for(s16 x0=-si; x0<=si-1; x0++)
2757 s16 si2 = rs - abs(x0);
2758 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2760 // Don't put mineral to every place
2768 /*if(isInArea(p, ar) == false)
2770 // Check only height
2771 if(y < 0 || y >= ar.Y)
2775 assert(data->vmanip.m_area.contains(p));
2777 // Just set it to air, it will be changed to
2779 u32 i = data->vmanip.m_area.index(p);
2780 MapNode *n = &data->vmanip.m_data[i];
2781 if(n->d == CONTENT_STONE)
2799 TimeTaker timer1("add mud");
2802 Add mud to the central chunk
2805 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2806 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2808 // Node position in 2d
2809 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2811 // Randomize mud amount
2812 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2814 // Find ground level
2815 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2818 If topmost node is grass, change it to mud.
2819 It might be if it was flown to there from a neighboring
2820 chunk and then converted.
2823 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2824 MapNode *n = &data->vmanip.m_data[i];
2825 if(n->d == CONTENT_GRASS)
2826 *n = MapNode(CONTENT_MUD);
2827 //n->d = CONTENT_MUD;
2835 v3s16 em = data->vmanip.m_area.getExtent();
2836 s16 y_start = surface_y+1;
2837 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2838 for(s16 y=y_start; y<=y_nodes_max; y++)
2840 if(mudcount >= mud_add_amount)
2843 MapNode &n = data->vmanip.m_data[i];
2844 n = MapNode(CONTENT_MUD);
2845 //n.d = CONTENT_MUD;
2848 data->vmanip.m_area.add_y(em, i, 1);
2860 TimeTaker timer1("flow mud");
2863 Flow mud away from steep edges
2866 // Limit area by 1 because mud is flown into neighbors.
2867 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2868 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2870 // Iterate a few times
2871 for(s16 k=0; k<3; k++)
2874 for(s16 x=mudflow_minpos;
2877 for(s16 z=mudflow_minpos;
2881 // Invert coordinates every 2nd iteration
2884 x = mudflow_maxpos - (x-mudflow_minpos);
2885 z = mudflow_maxpos - (z-mudflow_minpos);
2888 // Node position in 2d
2889 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2891 v3s16 em = data->vmanip.m_area.getExtent();
2892 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2899 for(; y>=y_nodes_min; y--)
2901 n = &data->vmanip.m_data[i];
2902 //if(content_walkable(n->d))
2904 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2907 data->vmanip.m_area.add_y(em, i, -1);
2910 // Stop if out of area
2911 //if(data->vmanip.m_area.contains(i) == false)
2915 /*// If not mud, do nothing to it
2916 MapNode *n = &data->vmanip.m_data[i];
2917 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2921 Don't flow it if the stuff under it is not mud
2925 data->vmanip.m_area.add_y(em, i2, -1);
2926 // Cancel if out of area
2927 if(data->vmanip.m_area.contains(i2) == false)
2929 MapNode *n2 = &data->vmanip.m_data[i2];
2930 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2934 // Make it exactly mud
2937 /*s16 recurse_count = 0;
2941 v3s16(0,0,1), // back
2942 v3s16(1,0,0), // right
2943 v3s16(0,0,-1), // front
2944 v3s16(-1,0,0), // left
2947 // Theck that upper is air or doesn't exist.
2948 // Cancel dropping if upper keeps it in place
2950 data->vmanip.m_area.add_y(em, i3, 1);
2951 if(data->vmanip.m_area.contains(i3) == true
2952 && content_walkable(data->vmanip.m_data[i3].d) == true)
2959 for(u32 di=0; di<4; di++)
2961 v3s16 dirp = dirs4[di];
2964 data->vmanip.m_area.add_p(em, i2, dirp);
2965 // Fail if out of area
2966 if(data->vmanip.m_area.contains(i2) == false)
2968 // Check that side is air
2969 MapNode *n2 = &data->vmanip.m_data[i2];
2970 if(content_walkable(n2->d))
2972 // Check that under side is air
2973 data->vmanip.m_area.add_y(em, i2, -1);
2974 if(data->vmanip.m_area.contains(i2) == false)
2976 n2 = &data->vmanip.m_data[i2];
2977 if(content_walkable(n2->d))
2979 /*// Check that under that is air (need a drop of 2)
2980 data->vmanip.m_area.add_y(em, i2, -1);
2981 if(data->vmanip.m_area.contains(i2) == false)
2983 n2 = &data->vmanip.m_data[i2];
2984 if(content_walkable(n2->d))
2986 // Loop further down until not air
2988 data->vmanip.m_area.add_y(em, i2, -1);
2989 // Fail if out of area
2990 if(data->vmanip.m_area.contains(i2) == false)
2992 n2 = &data->vmanip.m_data[i2];
2993 }while(content_walkable(n2->d) == false);
2994 // Loop one up so that we're in air
2995 data->vmanip.m_area.add_y(em, i2, 1);
2996 n2 = &data->vmanip.m_data[i2];
2998 // Move mud to new place
3000 // Set old place to be air
3001 *n = MapNode(CONTENT_AIR);
3017 TimeTaker timer1("add water");
3020 Add water to the central chunk (and a bit more)
3023 for(s16 x=0-data->max_spread_amount;
3024 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3026 for(s16 z=0-data->max_spread_amount;
3027 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3030 // Node position in 2d
3031 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3033 // Find ground level
3034 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3037 If ground level is over water level, skip.
3038 NOTE: This leaves caves near water without water,
3039 which looks especially crappy when the nearby water
3040 won't start flowing either for some reason
3042 /*if(surface_y > WATER_LEVEL)
3049 v3s16 em = data->vmanip.m_area.getExtent();
3050 u8 light = LIGHT_MAX;
3051 // Start at global water surface level
3052 s16 y_start = WATER_LEVEL;
3053 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3054 MapNode *n = &data->vmanip.m_data[i];
3056 for(s16 y=y_start; y>=y_nodes_min; y--)
3058 n = &data->vmanip.m_data[i];
3060 // Stop when there is no water and no air
3061 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3062 && n->d != CONTENT_WATER)
3068 // Make water only not in caves
3069 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3071 n->d = CONTENT_WATERSOURCE;
3072 //n->setLight(LIGHTBANK_DAY, light);
3074 // Add to transforming liquid queue (in case it'd
3076 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3077 data->transforming_liquid.push_back(p);
3081 data->vmanip.m_area.add_y(em, i, -1);
3093 /***********************
3095 ************************/
3099 //TimeTaker timer1("convert mud to sand");
3105 //s16 mud_add_amount = myrand_range(2, 4);
3106 //s16 mud_add_amount = 0;
3108 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3109 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3110 for(s16 x=0-data->max_spread_amount+1;
3111 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3113 for(s16 z=0-data->max_spread_amount+1;
3114 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3117 // Node position in 2d
3118 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3120 // Determine whether to have sand here
3121 double sandnoise = noise2d_perlin(
3122 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3123 data->seed+59420, 3, 0.50);
3125 bool have_sand = (sandnoise > -0.15);
3127 if(have_sand == false)
3130 // Find ground level
3131 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3133 if(surface_y > WATER_LEVEL + 2)
3137 v3s16 em = data->vmanip.m_area.getExtent();
3138 s16 y_start = surface_y;
3139 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3140 u32 not_sand_counter = 0;
3141 for(s16 y=y_start; y>=y_nodes_min; y--)
3143 MapNode *n = &data->vmanip.m_data[i];
3144 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3146 n->d = CONTENT_SAND;
3151 if(not_sand_counter > 3)
3155 data->vmanip.m_area.add_y(em, i, -1);
3167 //TimeTaker timer1("generate trees");
3173 // Divide area into parts
3175 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3176 double area = sidelen * sidelen;
3177 for(s16 x0=0; x0<div; x0++)
3178 for(s16 z0=0; z0<div; z0++)
3180 // Center position of part of division
3182 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3183 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3185 // Minimum edge of part of division
3187 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3188 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3190 // Maximum edge of part of division
3192 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3193 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3196 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3197 // Put trees in random places on part of division
3198 for(u32 i=0; i<tree_count; i++)
3200 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3201 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3202 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3203 // Don't make a tree under water level
3206 // Don't make a tree so high that it doesn't fit
3207 if(y > y_nodes_max - 6)
3211 Trees grow only on mud and grass
3214 u32 i = data->vmanip.m_area.index(v3s16(p));
3215 MapNode *n = &data->vmanip.m_data[i];
3216 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3221 make_tree(data->vmanip, p);
3224 /*u32 tree_max = relative_area / 60;
3225 //u32 count = myrand_range(0, tree_max);
3226 for(u32 i=0; i<count; i++)
3228 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3229 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3230 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3231 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3232 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3233 // Don't make a tree under water level
3238 make_tree(data->vmanip, p);
3248 //TimeTaker timer1("grow grass");
3254 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3255 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3256 for(s16 x=0-data->max_spread_amount;
3257 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3259 for(s16 z=0-data->max_spread_amount;
3260 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3263 // Node position in 2d
3264 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3267 Find the lowest surface to which enough light ends up
3270 Basically just wait until not air and not leaves.
3274 v3s16 em = data->vmanip.m_area.getExtent();
3275 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3277 // Go to ground level
3278 for(y=y_nodes_max; y>=y_nodes_min; y--)
3280 MapNode &n = data->vmanip.m_data[i];
3281 if(n.d != CONTENT_AIR
3282 && n.d != CONTENT_LEAVES)
3284 data->vmanip.m_area.add_y(em, i, -1);
3286 if(y >= y_nodes_min)
3289 surface_y = y_nodes_min;
3292 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3293 MapNode *n = &data->vmanip.m_data[i];
3294 if(n->d == CONTENT_MUD)
3295 n->d = CONTENT_GRASS;
3302 Initial lighting (sunlight)
3305 core::map<v3s16, bool> light_sources;
3308 // 750ms @cs=8, can't optimize more
3309 TimeTaker timer1("initial lighting");
3311 // NOTE: This is no used... umm... for some reason!
3314 Go through the edges and add all nodes that have light to light_sources
3318 for(s16 i=0; i<4; i++)
3320 for(s16 j=lighting_min_d;
3327 if(i == 0 || i == 1)
3329 x = (i==0) ? lighting_min_d : lighting_max_d;
3338 z = (i==0) ? lighting_min_d : lighting_max_d;
3345 // Node position in 2d
3346 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3349 v3s16 em = data->vmanip.m_area.getExtent();
3350 s16 y_start = y_nodes_max;
3351 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3352 for(s16 y=y_start; y>=y_nodes_min; y--)
3354 MapNode *n = &data->vmanip.m_data[i];
3355 if(n->getLight(LIGHTBANK_DAY) != 0)
3357 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3359 //NOTE: This is broken, at least the index has to
3368 Go through the edges and apply sunlight to them, not caring
3373 for(s16 i=0; i<4; i++)
3375 for(s16 j=lighting_min_d;
3382 if(i == 0 || i == 1)
3384 x = (i==0) ? lighting_min_d : lighting_max_d;
3393 z = (i==0) ? lighting_min_d : lighting_max_d;
3400 // Node position in 2d
3401 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3403 // Loop from top to down
3405 u8 light = LIGHT_SUN;
3406 v3s16 em = data->vmanip.m_area.getExtent();
3407 s16 y_start = y_nodes_max;
3408 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3409 for(s16 y=y_start; y>=y_nodes_min; y--)
3411 MapNode *n = &data->vmanip.m_data[i];
3412 if(light_propagates_content(n->d) == false)
3416 else if(light != LIGHT_SUN
3417 || sunlight_propagates_content(n->d) == false)
3423 n->setLight(LIGHTBANK_DAY, light);
3424 n->setLight(LIGHTBANK_NIGHT, 0);
3428 // Insert light source
3429 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3432 // Increment index by y
3433 data->vmanip.m_area.add_y(em, i, -1);
3439 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3440 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3441 /*for(s16 x=0-data->max_spread_amount+1;
3442 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3444 for(s16 z=0-data->max_spread_amount+1;
3445 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3449 This has to be 1 smaller than the actual area, because
3450 neighboring nodes are checked.
3452 for(s16 x=lighting_min_d+1;
3453 x<=lighting_max_d-1;
3455 for(s16 z=lighting_min_d+1;
3456 z<=lighting_max_d-1;
3459 // Node position in 2d
3460 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3463 Apply initial sunlight
3466 u8 light = LIGHT_SUN;
3467 bool add_to_sources = false;
3468 v3s16 em = data->vmanip.m_area.getExtent();
3469 s16 y_start = y_nodes_max;
3470 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3471 for(s16 y=y_start; y>=y_nodes_min; y--)
3473 MapNode *n = &data->vmanip.m_data[i];
3475 if(light_propagates_content(n->d) == false)
3479 else if(light != LIGHT_SUN
3480 || sunlight_propagates_content(n->d) == false)
3486 // This doesn't take much time
3487 if(add_to_sources == false)
3490 Check sides. If side is not air or water, start
3491 adding to light_sources.
3494 v3s16(0,0,1), // back
3495 v3s16(1,0,0), // right
3496 v3s16(0,0,-1), // front
3497 v3s16(-1,0,0), // left
3499 for(u32 di=0; di<4; di++)
3501 v3s16 dirp = dirs4[di];
3503 data->vmanip.m_area.add_p(em, i2, dirp);
3504 MapNode *n2 = &data->vmanip.m_data[i2];
3506 n2->d != CONTENT_AIR
3507 && n2->d != CONTENT_WATERSOURCE
3508 && n2->d != CONTENT_WATER
3510 add_to_sources = true;
3516 n->setLight(LIGHTBANK_DAY, light);
3517 n->setLight(LIGHTBANK_NIGHT, 0);
3519 // This doesn't take much time
3520 if(light != 0 && add_to_sources)
3522 // Insert light source
3523 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3526 // Increment index by y
3527 data->vmanip.m_area.add_y(em, i, -1);
3535 // Spread light around
3537 TimeTaker timer("makeChunk() spreadLight");
3538 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3545 timer_generate.stop();
3548 //###################################################################
3549 //###################################################################
3550 //###################################################################
3551 //###################################################################
3552 //###################################################################
3553 //###################################################################
3554 //###################################################################
3555 //###################################################################
3556 //###################################################################
3557 //###################################################################
3558 //###################################################################
3559 //###################################################################
3560 //###################################################################
3561 //###################################################################
3562 //###################################################################
3564 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3566 if(m_chunksize == 0)
3574 // The distance how far into the neighbors the generator is allowed to go.
3575 s16 max_spread_amount_sectors = 2;
3576 assert(max_spread_amount_sectors <= m_chunksize);
3577 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3579 s16 y_blocks_min = -4;
3580 s16 y_blocks_max = 3;
3582 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3583 s16 sectorpos_base_size = m_chunksize;
3585 v2s16 sectorpos_bigbase =
3586 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3587 s16 sectorpos_bigbase_size =
3588 sectorpos_base_size + 2 * max_spread_amount_sectors;
3591 const s16 limit = MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
3592 if(sectorpos_bigbase.X < -limit
3593 || sectorpos_bigbase.X + sectorpos_bigbase_size >= limit
3594 || sectorpos_bigbase.Y < -limit
3595 || sectorpos_bigbase.Y + sectorpos_bigbase_size >= limit)
3602 data.chunkpos = chunkpos;
3603 data.y_blocks_min = y_blocks_min;
3604 data.y_blocks_max = y_blocks_max;
3605 data.sectorpos_base = sectorpos_base;
3606 data.sectorpos_base_size = sectorpos_base_size;
3607 data.sectorpos_bigbase = sectorpos_bigbase;
3608 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3609 data.max_spread_amount = max_spread_amount;
3612 Create the whole area of this and the neighboring chunks
3615 TimeTaker timer("initChunkMake() create area");
3617 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3618 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3620 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3621 ServerMapSector *sector = createSector(sectorpos);
3624 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3626 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3627 MapBlock *block = createBlock(blockpos);
3629 // Lighting won't be calculated
3630 //block->setLightingExpired(true);
3631 // Lighting will be calculated
3632 block->setLightingExpired(false);
3635 Block gets sunlight if this is true.
3637 This should be set to true when the top side of a block
3638 is completely exposed to the sky.
3640 Actually this doesn't matter now because the
3641 initial lighting is done here.
3643 block->setIsUnderground(y != y_blocks_max);
3649 Now we have a big empty area.
3651 Make a ManualMapVoxelManipulator that contains this and the
3655 v3s16 bigarea_blocks_min(
3656 sectorpos_bigbase.X,
3660 v3s16 bigarea_blocks_max(
3661 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3663 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3666 data.vmanip.setMap(this);
3669 TimeTaker timer("initChunkMake() initialEmerge");
3670 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3675 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3676 core::map<v3s16, MapBlock*> &changed_blocks)
3682 Blit generated stuff to map
3686 //TimeTaker timer("generateChunkRaw() blitBackAll");
3687 data.vmanip.blitBackAll(&changed_blocks);
3691 Update day/night difference cache of the MapBlocks
3694 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3695 i.atEnd() == false; i++)
3697 MapBlock *block = i.getNode()->getValue();
3698 block->updateDayNightDiff();
3703 Copy transforming liquid information
3705 while(data.transforming_liquid.size() > 0)
3707 v3s16 p = data.transforming_liquid.pop_front();
3708 m_transforming_liquid.push_back(p);
3712 Add random objects to blocks
3715 for(s16 x=0; x<data.sectorpos_base_size; x++)
3716 for(s16 z=0; z<data.sectorpos_base_size; z++)
3718 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3719 ServerMapSector *sector = createSector(sectorpos);
3722 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3724 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3725 MapBlock *block = createBlock(blockpos);
3726 addRandomObjects(block);
3732 Create chunk metadata
3735 for(s16 x=-1; x<=1; x++)
3736 for(s16 y=-1; y<=1; y++)
3738 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3739 // Add chunk meta information
3740 MapChunk *chunk = getChunk(chunkpos0);
3743 chunk = new MapChunk();
3744 m_chunks.insert(chunkpos0, chunk);
3746 //chunk->setIsVolatile(true);
3747 if(chunk->getGenLevel() > GENERATED_PARTLY)
3748 chunk->setGenLevel(GENERATED_PARTLY);
3752 Set central chunk non-volatile
3754 MapChunk *chunk = getChunk(data.chunkpos);
3757 //chunk->setIsVolatile(false);
3758 chunk->setGenLevel(GENERATED_FULLY);
3761 Save changed parts of map
3770 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3771 core::map<v3s16, MapBlock*> &changed_blocks,
3774 DSTACK(__FUNCTION_NAME);
3777 Don't generate if already fully generated
3781 MapChunk *chunk = getChunk(chunkpos);
3782 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3784 dstream<<"generateChunkRaw(): Chunk "
3785 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3786 <<" already generated"<<std::endl;
3791 dstream<<"generateChunkRaw(): Generating chunk "
3792 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3795 TimeTaker timer("generateChunkRaw()");
3799 // Initialize generation
3800 initChunkMake(data, chunkpos);
3805 // Finalize generation
3806 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3809 Return central chunk (which was requested)
3815 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3816 core::map<v3s16, MapBlock*> &changed_blocks)
3818 dstream<<"generateChunk(): Generating chunk "
3819 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3822 /*for(s16 x=-1; x<=1; x++)
3823 for(s16 y=-1; y<=1; y++)*/
3824 for(s16 x=-0; x<=0; x++)
3825 for(s16 y=-0; y<=0; y++)
3827 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3828 MapChunk *chunk = getChunk(chunkpos0);
3829 // Skip if already generated
3830 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3832 generateChunkRaw(chunkpos0, changed_blocks);
3835 assert(chunkNonVolatile(chunkpos1));
3837 MapChunk *chunk = getChunk(chunkpos1);
3842 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3844 DSTACKF("%s: p2d=(%d,%d)",
3849 Check if it exists already in memory
3851 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3856 Try to load it from disk (with blocks)
3858 if(loadSectorFull(p2d) == true)
3860 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3863 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3864 throw InvalidPositionException("");
3870 Do not create over-limit
3872 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3873 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3874 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3875 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3876 throw InvalidPositionException("createSector(): pos. over limit");
3879 Generate blank sector
3882 sector = new ServerMapSector(this, p2d);
3884 // Sector position on map in nodes
3885 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3890 m_sectors.insert(p2d, sector);
3896 MapSector * ServerMap::emergeSector(v2s16 p2d,
3897 core::map<v3s16, MapBlock*> &changed_blocks)
3899 DSTACK("%s: p2d=(%d,%d)",
3906 v2s16 chunkpos = sector_to_chunk(p2d);
3907 /*bool chunk_nonvolatile = false;
3908 MapChunk *chunk = getChunk(chunkpos);
3909 if(chunk && chunk->getIsVolatile() == false)
3910 chunk_nonvolatile = true;*/
3911 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3914 If chunk is not fully generated, generate chunk
3916 if(chunk_nonvolatile == false)
3918 // Generate chunk and neighbors
3919 generateChunk(chunkpos, changed_blocks);
3923 Return sector if it exists now
3925 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3930 Try to load it from disk
3932 if(loadSectorFull(p2d) == true)
3934 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3937 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3938 throw InvalidPositionException("");
3944 generateChunk should have generated the sector
3948 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3949 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3953 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3955 return createSector(p2d);
3960 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3963 generateChunkRaw(chunkpos, changed_blocks, true);
3966 Return sector if it exists now
3968 sector = getSectorNoGenerateNoEx(p2d);
3972 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3980 //return generateSector();
3985 NOTE: This is not used for main map generation, only for blocks
3986 that are very high or low
3988 MapBlock * ServerMap::generateBlock(
3990 MapBlock *original_dummy,
3991 ServerMapSector *sector,
3992 core::map<v3s16, MapBlock*> &changed_blocks,
3993 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3996 DSTACKF("%s: p=(%d,%d,%d)",
4000 // If chunks are disabled
4001 /*if(m_chunksize == 0)
4003 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
4004 <<"not generating."<<std::endl;
4008 /*dstream<<"generateBlock(): "
4009 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4012 MapBlock *block = original_dummy;
4014 v2s16 p2d(p.X, p.Z);
4016 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4019 Do not generate over-limit
4021 if(blockpos_over_limit(p))
4023 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4024 throw InvalidPositionException("generateBlock(): pos. over limit");
4028 If block doesn't exist, create one.
4029 If it exists, it is a dummy. In that case unDummify() it.
4031 NOTE: This already sets the map as the parent of the block
4035 block = sector->createBlankBlockNoInsert(block_y);
4039 // Remove the block so that nobody can get a half-generated one.
4040 sector->removeBlock(block);
4041 // Allocate the block to contain the generated data
4047 Generate a completely empty block
4049 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4050 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4052 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4056 block->setNode(v3s16(x0,y0,z0), n);
4061 Generate a proper block
4064 u8 water_material = CONTENT_WATERSOURCE;
4066 s32 lowest_ground_y = 32767;
4067 s32 highest_ground_y = -32768;
4069 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4070 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4072 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4074 //s16 surface_y = 0;
4076 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4078 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4080 // If chunks are disabled
4081 if(m_chunksize == 0)
4082 surface_y = WATER_LEVEL + 1;
4084 if(surface_y < lowest_ground_y)
4085 lowest_ground_y = surface_y;
4086 if(surface_y > highest_ground_y)
4087 highest_ground_y = surface_y;
4089 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4091 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4093 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4098 NOTE: If there are some man-made structures above the
4099 newly created block, they won't be taken into account.
4101 if(real_y > surface_y)
4102 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4108 // If node is over heightmap y, it's air or water
4109 if(real_y > surface_y)
4111 // If under water level, it's water
4112 if(real_y < WATER_LEVEL)
4114 n.d = water_material;
4115 n.setLight(LIGHTBANK_DAY,
4116 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4118 Add to transforming liquid queue (in case it'd
4121 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4122 m_transforming_liquid.push_back(real_pos);
4128 // Else it's ground or caves (air)
4131 // If it's surface_depth under ground, it's stone
4132 if(real_y <= surface_y - surface_depth)
4134 n.d = CONTENT_STONE;
4138 // It is mud if it is under the first ground
4139 // level or under water
4140 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4146 n.d = CONTENT_GRASS;
4149 //n.d = CONTENT_MUD;
4151 /*// If under water level, it's mud
4152 if(real_y < WATER_LEVEL)
4154 // Only the topmost node is grass
4155 else if(real_y <= surface_y - 1)
4158 n.d = CONTENT_GRASS;*/
4162 block->setNode(v3s16(x0,y0,z0), n);
4167 Calculate some helper variables
4170 // Completely underground if the highest part of block is under lowest
4172 // This has to be very sure; it's probably one too strict now but
4173 // that's just better.
4174 bool completely_underground =
4175 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4177 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4179 bool mostly_underwater_surface = false;
4180 if(highest_ground_y < WATER_LEVEL
4181 && some_part_underground && !completely_underground)
4182 mostly_underwater_surface = true;
4185 Get local attributes
4188 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4190 float caves_amount = 0.5;
4195 NOTE: BEWARE: Too big amount of attribute points slows verything
4197 1 interpolation from 5000 points takes 2-3ms.
4199 //TimeTaker timer("generateBlock() local attribute retrieval");
4200 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4201 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4202 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4206 //dstream<<"generateBlock(): Done"<<std::endl;
4212 // Initialize temporary table
4213 const s32 ued = MAP_BLOCKSIZE;
4214 bool underground_emptiness[ued*ued*ued];
4215 for(s32 i=0; i<ued*ued*ued; i++)
4217 underground_emptiness[i] = 0;
4224 Initialize orp and ors. Try to find if some neighboring
4225 MapBlock has a tunnel ended in its side
4229 (float)(myrand()%ued)+0.5,
4230 (float)(myrand()%ued)+0.5,
4231 (float)(myrand()%ued)+0.5
4234 bool found_existing = false;
4240 for(s16 y=0; y<ued; y++)
4241 for(s16 x=0; x<ued; x++)
4243 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4244 if(getNode(ap).d == CONTENT_AIR)
4246 orp = v3f(x+1,y+1,0);
4247 found_existing = true;
4248 goto continue_generating;
4252 catch(InvalidPositionException &e){}
4258 for(s16 y=0; y<ued; y++)
4259 for(s16 x=0; x<ued; x++)
4261 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4262 if(getNode(ap).d == CONTENT_AIR)
4264 orp = v3f(x+1,y+1,ued-1);
4265 found_existing = true;
4266 goto continue_generating;
4270 catch(InvalidPositionException &e){}
4276 for(s16 y=0; y<ued; y++)
4277 for(s16 z=0; z<ued; z++)
4279 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4280 if(getNode(ap).d == CONTENT_AIR)
4282 orp = v3f(0,y+1,z+1);
4283 found_existing = true;
4284 goto continue_generating;
4288 catch(InvalidPositionException &e){}
4294 for(s16 y=0; y<ued; y++)
4295 for(s16 z=0; z<ued; z++)
4297 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4298 if(getNode(ap).d == CONTENT_AIR)
4300 orp = v3f(ued-1,y+1,z+1);
4301 found_existing = true;
4302 goto continue_generating;
4306 catch(InvalidPositionException &e){}
4312 for(s16 x=0; x<ued; x++)
4313 for(s16 z=0; z<ued; z++)
4315 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4316 if(getNode(ap).d == CONTENT_AIR)
4318 orp = v3f(x+1,0,z+1);
4319 found_existing = true;
4320 goto continue_generating;
4324 catch(InvalidPositionException &e){}
4330 for(s16 x=0; x<ued; x++)
4331 for(s16 z=0; z<ued; z++)
4333 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4334 if(getNode(ap).d == CONTENT_AIR)
4336 orp = v3f(x+1,ued-1,z+1);
4337 found_existing = true;
4338 goto continue_generating;
4342 catch(InvalidPositionException &e){}
4344 continue_generating:
4347 Choose whether to actually generate cave
4349 bool do_generate_caves = true;
4350 // Don't generate if no part is underground
4351 if(!some_part_underground)
4353 do_generate_caves = false;
4355 // Don't generate if mostly underwater surface
4356 /*else if(mostly_underwater_surface)
4358 do_generate_caves = false;
4360 // Partly underground = cave
4361 else if(!completely_underground)
4363 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4365 // Found existing cave underground
4366 else if(found_existing && completely_underground)
4368 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4370 // Underground and no caves found
4373 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4376 if(do_generate_caves)
4379 Generate some tunnel starting from orp and ors
4381 for(u16 i=0; i<3; i++)
4384 (float)(myrand()%ued)+0.5,
4385 (float)(myrand()%ued)+0.5,
4386 (float)(myrand()%ued)+0.5
4390 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4394 for(float f=0; f<1.0; f+=0.04)
4396 v3f fp = orp + vec * f;
4397 v3s16 cp(fp.X, fp.Y, fp.Z);
4399 s16 d1 = d0 + rs - 1;
4400 for(s16 z0=d0; z0<=d1; z0++)
4402 s16 si = rs - abs(z0);
4403 for(s16 x0=-si; x0<=si-1; x0++)
4405 s16 si2 = rs - abs(x0);
4406 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4412 if(isInArea(p, ued) == false)
4414 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4426 // Set to true if has caves.
4427 // Set when some non-air is changed to air when making caves.
4428 bool has_caves = false;
4431 Apply temporary cave data to block
4434 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4435 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4437 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4439 MapNode n = block->getNode(v3s16(x0,y0,z0));
4442 if(underground_emptiness[
4443 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4444 +ued*(y0*ued/MAP_BLOCKSIZE)
4445 +(x0*ued/MAP_BLOCKSIZE)])
4447 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4456 block->setNode(v3s16(x0,y0,z0), n);
4461 This is used for guessing whether or not the block should
4462 receive sunlight from the top if the block above doesn't exist
4464 block->setIsUnderground(completely_underground);
4467 Force lighting update if some part of block is partly
4468 underground and has caves.
4470 /*if(some_part_underground && !completely_underground && has_caves)
4472 //dstream<<"Half-ground caves"<<std::endl;
4473 lighting_invalidated_blocks[block->getPos()] = block;
4476 // DEBUG: Always update lighting
4477 //lighting_invalidated_blocks[block->getPos()] = block;
4483 if(some_part_underground)
4485 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4490 for(s16 i=0; i<underground_level/4 + 1; i++)
4492 if(myrand()%50 == 0)
4495 (myrand()%(MAP_BLOCKSIZE-2))+1,
4496 (myrand()%(MAP_BLOCKSIZE-2))+1,
4497 (myrand()%(MAP_BLOCKSIZE-2))+1
4503 for(u16 i=0; i<27; i++)
4505 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4507 block->setNode(cp+g_27dirs[i], n);
4515 u16 coal_amount = 30;
4516 u16 coal_rareness = 60 / coal_amount;
4517 if(coal_rareness == 0)
4519 if(myrand()%coal_rareness == 0)
4521 u16 a = myrand() % 16;
4522 u16 amount = coal_amount * a*a*a / 1000;
4523 for(s16 i=0; i<amount; i++)
4526 (myrand()%(MAP_BLOCKSIZE-2))+1,
4527 (myrand()%(MAP_BLOCKSIZE-2))+1,
4528 (myrand()%(MAP_BLOCKSIZE-2))+1
4532 n.d = CONTENT_STONE;
4533 n.param = MINERAL_COAL;
4535 for(u16 i=0; i<27; i++)
4537 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4539 block->setNode(cp+g_27dirs[i], n);
4547 //TODO: change to iron_amount or whatever
4548 u16 iron_amount = 15;
4549 u16 iron_rareness = 60 / iron_amount;
4550 if(iron_rareness == 0)
4552 if(myrand()%iron_rareness == 0)
4554 u16 a = myrand() % 16;
4555 u16 amount = iron_amount * a*a*a / 1000;
4556 for(s16 i=0; i<amount; i++)
4559 (myrand()%(MAP_BLOCKSIZE-2))+1,
4560 (myrand()%(MAP_BLOCKSIZE-2))+1,
4561 (myrand()%(MAP_BLOCKSIZE-2))+1
4565 n.d = CONTENT_STONE;
4566 n.param = MINERAL_IRON;
4568 for(u16 i=0; i<27; i++)
4570 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4572 block->setNode(cp+g_27dirs[i], n);
4579 Create a few rats in empty blocks underground
4581 if(completely_underground)
4583 //for(u16 i=0; i<2; i++)
4586 (myrand()%(MAP_BLOCKSIZE-2))+1,
4587 (myrand()%(MAP_BLOCKSIZE-2))+1,
4588 (myrand()%(MAP_BLOCKSIZE-2))+1
4591 // Check that the place is empty
4592 //if(!is_ground_content(block->getNode(cp).d))
4595 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4596 block->addObject(obj);
4601 #endif // end of proper block generation
4604 Add block to sector.
4606 sector->insertBlock(block);
4608 // Lighting is invalid after generation.
4609 block->setLightingExpired(true);
4616 <<"lighting_invalidated_blocks.size()"
4620 <<" "<<lighting_invalidated_blocks.size()
4622 <<", "<<completely_underground
4623 <<", "<<some_part_underground
4630 MapBlock * ServerMap::createBlock(v3s16 p)
4632 DSTACKF("%s: p=(%d,%d,%d)",
4633 __FUNCTION_NAME, p.X, p.Y, p.Z);
4636 Do not create over-limit
4638 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4639 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4640 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4641 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4642 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4643 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4644 throw InvalidPositionException("createBlock(): pos. over limit");
4646 v2s16 p2d(p.X, p.Z);
4649 This will create or load a sector if not found in memory.
4650 If block exists on disk, it will be loaded.
4652 NOTE: On old save formats, this will be slow, as it generates
4653 lighting on blocks for them.
4655 ServerMapSector *sector;
4657 sector = (ServerMapSector*)createSector(p2d);
4658 assert(sector->getId() == MAPSECTOR_SERVER);
4660 catch(InvalidPositionException &e)
4662 dstream<<"createBlock: createSector() failed"<<std::endl;
4666 NOTE: This should not be done, or at least the exception
4667 should not be passed on as std::exception, because it
4668 won't be catched at all.
4670 /*catch(std::exception &e)
4672 dstream<<"createBlock: createSector() failed: "
4673 <<e.what()<<std::endl;
4678 Try to get a block from the sector
4681 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4685 block = sector->createBlankBlock(block_y);
4689 MapBlock * ServerMap::emergeBlock(
4691 bool only_from_disk,
4692 core::map<v3s16, MapBlock*> &changed_blocks,
4693 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4696 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4698 p.X, p.Y, p.Z, only_from_disk);
4701 Do not generate over-limit
4703 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4704 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4705 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4706 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4707 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4708 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4709 throw InvalidPositionException("emergeBlock(): pos. over limit");
4711 v2s16 p2d(p.X, p.Z);
4714 This will create or load a sector if not found in memory.
4715 If block exists on disk, it will be loaded.
4717 ServerMapSector *sector;
4719 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4720 assert(sector->getId() == MAPSECTOR_SERVER);
4722 catch(InvalidPositionException &e)
4724 dstream<<"emergeBlock: emergeSector() failed: "
4725 <<e.what()<<std::endl;
4726 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4728 <<"You could try to delete it."<<std::endl;
4731 catch(VersionMismatchException &e)
4733 dstream<<"emergeBlock: emergeSector() failed: "
4734 <<e.what()<<std::endl;
4735 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4737 <<"You could try to delete it."<<std::endl;
4741 NOTE: This should not be done, or at least the exception
4742 should not be passed on as std::exception, because it
4743 won't be catched at all.
4745 /*catch(std::exception &e)
4747 dstream<<"emergeBlock: emergeSector() failed: "
4748 <<e.what()<<std::endl;
4749 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4751 <<"You could try to delete it."<<std::endl;
4756 Try to get a block from the sector
4759 bool does_not_exist = false;
4760 bool lighting_expired = false;
4761 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4765 does_not_exist = true;
4767 else if(block->isDummy() == true)
4769 does_not_exist = true;
4771 else if(block->getLightingExpired())
4773 lighting_expired = true;
4778 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4783 If block was not found on disk and not going to generate a
4784 new one, make sure there is a dummy block in place.
4786 if(only_from_disk && (does_not_exist || lighting_expired))
4788 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4792 // Create dummy block
4793 block = new MapBlock(this, p, true);
4795 // Add block to sector
4796 sector->insertBlock(block);
4802 //dstream<<"Not found on disk, generating."<<std::endl;
4804 //TimeTaker("emergeBlock() generate");
4806 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4809 If the block doesn't exist, generate the block.
4813 block = generateBlock(p, block, sector, changed_blocks,
4814 lighting_invalidated_blocks);
4817 if(lighting_expired)
4819 lighting_invalidated_blocks.insert(p, block);
4823 Initially update sunlight
4827 core::map<v3s16, bool> light_sources;
4828 bool black_air_left = false;
4829 bool bottom_invalid =
4830 block->propagateSunlight(light_sources, true,
4831 &black_air_left, true);
4833 // If sunlight didn't reach everywhere and part of block is
4834 // above ground, lighting has to be properly updated
4835 //if(black_air_left && some_part_underground)
4838 lighting_invalidated_blocks[block->getPos()] = block;
4843 lighting_invalidated_blocks[block->getPos()] = block;
4850 s16 ServerMap::findGroundLevel(v2s16 p2d)
4853 Uh, just do something random...
4855 // Find existing map from top to down
4858 v3s16 p(p2d.X, max, p2d.Y);
4859 for(; p.Y>min; p.Y--)
4861 MapNode n = getNodeNoEx(p);
4862 if(n.d != CONTENT_IGNORE)
4867 // If this node is not air, go to plan b
4868 if(getNodeNoEx(p).d != CONTENT_AIR)
4870 // Search existing walkable and return it
4871 for(; p.Y>min; p.Y--)
4873 MapNode n = getNodeNoEx(p);
4874 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4880 Plan B: Get from map generator perlin noise function
4882 // This won't work if proper generation is disabled
4883 if(m_chunksize == 0)
4884 return WATER_LEVEL+2;
4885 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4889 void ServerMap::createDirs(std::string path)
4891 if(fs::CreateAllDirs(path) == false)
4893 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4894 <<"\""<<path<<"\""<<std::endl;
4895 throw BaseException("ServerMap failed to create directory");
4899 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4905 snprintf(cc, 9, "%.4x%.4x",
4906 (unsigned int)pos.X&0xffff,
4907 (unsigned int)pos.Y&0xffff);
4909 return m_savedir + "/sectors/" + cc;
4911 snprintf(cc, 9, "%.3x/%.3x",
4912 (unsigned int)pos.X&0xfff,
4913 (unsigned int)pos.Y&0xfff);
4915 return m_savedir + "/sectors2/" + cc;
4921 v2s16 ServerMap::getSectorPos(std::string dirname)
4925 size_t spos = dirname.rfind('/') + 1;
4926 assert(spos != std::string::npos);
4927 if(dirname.size() - spos == 8)
4930 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4932 else if(dirname.size() - spos == 3)
4935 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4936 // Sign-extend the 12 bit values up to 16 bits...
4937 if(x&0x800) x|=0xF000;
4938 if(y&0x800) y|=0xF000;
4945 v2s16 pos((s16)x, (s16)y);
4949 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4951 v2s16 p2d = getSectorPos(sectordir);
4953 if(blockfile.size() != 4){
4954 throw InvalidFilenameException("Invalid block filename");
4957 int r = sscanf(blockfile.c_str(), "%4x", &y);
4959 throw InvalidFilenameException("Invalid block filename");
4960 return v3s16(p2d.X, y, p2d.Y);
4963 void ServerMap::save(bool only_changed)
4965 DSTACK(__FUNCTION_NAME);
4966 if(m_map_saving_enabled == false)
4968 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4972 if(only_changed == false)
4973 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4976 if(only_changed == false || m_map_metadata_changed)
4981 // Disable saving chunk metadata if chunks are disabled
4982 if(m_chunksize != 0)
4984 if(only_changed == false || anyChunkModified())
4988 u32 sector_meta_count = 0;
4989 u32 block_count = 0;
4992 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4994 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4995 for(; i.atEnd() == false; i++)
4997 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4998 assert(sector->getId() == MAPSECTOR_SERVER);
5000 if(sector->differs_from_disk || only_changed == false)
5002 saveSectorMeta(sector);
5003 sector_meta_count++;
5005 core::list<MapBlock*> blocks;
5006 sector->getBlocks(blocks);
5007 core::list<MapBlock*>::Iterator j;
5008 for(j=blocks.begin(); j!=blocks.end(); j++)
5010 MapBlock *block = *j;
5011 if(block->getChangedFlag() || only_changed == false)
5016 /*dstream<<"ServerMap: Written block ("
5017 <<block->getPos().X<<","
5018 <<block->getPos().Y<<","
5019 <<block->getPos().Z<<")"
5028 Only print if something happened or saved whole map
5030 if(only_changed == false || sector_meta_count != 0
5031 || block_count != 0)
5033 dstream<<DTIME<<"ServerMap: Written: "
5034 <<sector_meta_count<<" sector metadata files, "
5035 <<block_count<<" block files"
5041 // NOTE: Doing this is insane. Deprecated and probably broken.
5042 void ServerMap::loadAll()
5044 DSTACK(__FUNCTION_NAME);
5045 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5050 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5052 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5054 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5057 s32 printed_counter = -100000;
5058 s32 count = list.size();
5060 std::vector<fs::DirListNode>::iterator i;
5061 for(i=list.begin(); i!=list.end(); i++)
5063 if(counter > printed_counter + 10)
5065 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5066 printed_counter = counter;
5070 MapSector *sector = NULL;
5072 // We want directories
5076 sector = loadSectorMeta(i->name);
5078 catch(InvalidFilenameException &e)
5080 // This catches unknown crap in directory
5083 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5084 (m_savedir+"/sectors/"+i->name);
5085 std::vector<fs::DirListNode>::iterator i2;
5086 for(i2=list2.begin(); i2!=list2.end(); i2++)
5092 loadBlock(i->name, i2->name, sector);
5094 catch(InvalidFilenameException &e)
5096 // This catches unknown crap in directory
5100 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5105 void ServerMap::saveMasterHeightmap()
5107 DSTACK(__FUNCTION_NAME);
5109 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5111 createDir(m_savedir);
5113 /*std::string fullpath = m_savedir + "/master_heightmap";
5114 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5115 if(o.good() == false)
5116 throw FileNotGoodException("Cannot open master heightmap");*/
5118 // Format used for writing
5119 //u8 version = SER_FMT_VER_HIGHEST;
5122 void ServerMap::loadMasterHeightmap()
5124 DSTACK(__FUNCTION_NAME);
5126 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5128 /*std::string fullpath = m_savedir + "/master_heightmap";
5129 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5130 if(is.good() == false)
5131 throw FileNotGoodException("Cannot open master heightmap");*/
5135 void ServerMap::saveMapMeta()
5137 DSTACK(__FUNCTION_NAME);
5139 dstream<<"INFO: ServerMap::saveMapMeta(): "
5140 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5143 createDirs(m_savedir);
5145 std::string fullpath = m_savedir + "/map_meta.txt";
5146 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5147 if(os.good() == false)
5149 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5150 <<"could not open"<<fullpath<<std::endl;
5151 throw FileNotGoodException("Cannot open chunk metadata");
5155 params.setU64("seed", m_seed);
5156 params.setS32("chunksize", m_chunksize);
5158 params.writeLines(os);
5160 os<<"[end_of_params]\n";
5162 m_map_metadata_changed = false;
5165 void ServerMap::loadMapMeta()
5167 DSTACK(__FUNCTION_NAME);
5169 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5172 std::string fullpath = m_savedir + "/map_meta.txt";
5173 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5174 if(is.good() == false)
5176 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5177 <<"could not open"<<fullpath<<std::endl;
5178 throw FileNotGoodException("Cannot open map metadata");
5186 throw SerializationError
5187 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5189 std::getline(is, line);
5190 std::string trimmedline = trim(line);
5191 if(trimmedline == "[end_of_params]")
5193 params.parseConfigLine(line);
5196 m_seed = params.getU64("seed");
5197 m_chunksize = params.getS32("chunksize");
5199 dstream<<"INFO: ServerMap::loadMapMeta(): "
5200 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5204 void ServerMap::saveChunkMeta()
5206 DSTACK(__FUNCTION_NAME);
5208 // This should not be called if chunks are disabled.
5209 assert(m_chunksize != 0);
5211 u32 count = m_chunks.size();
5213 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5214 <<count<<" chunks"<<std::endl;
5216 createDirs(m_savedir);
5218 std::string fullpath = m_savedir + "/chunk_meta";
5219 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5220 if(os.good() == false)
5222 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5223 <<"could not open"<<fullpath<<std::endl;
5224 throw FileNotGoodException("Cannot open chunk metadata");
5230 os.write((char*)&version, 1);
5235 writeU32(buf, count);
5236 os.write((char*)buf, 4);
5238 for(core::map<v2s16, MapChunk*>::Iterator
5239 i = m_chunks.getIterator();
5240 i.atEnd()==false; i++)
5242 v2s16 p = i.getNode()->getKey();
5243 MapChunk *chunk = i.getNode()->getValue();
5246 os.write((char*)buf, 4);
5248 chunk->serialize(os, version);
5251 setChunksNonModified();
5254 void ServerMap::loadChunkMeta()
5256 DSTACK(__FUNCTION_NAME);
5258 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5261 std::string fullpath = m_savedir + "/chunk_meta";
5262 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5263 if(is.good() == false)
5265 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5266 <<"could not open"<<fullpath<<std::endl;
5267 throw FileNotGoodException("Cannot open chunk metadata");
5273 is.read((char*)&version, 1);
5278 is.read((char*)buf, 4);
5279 u32 count = readU32(buf);
5281 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5282 <<count<<" chunks"<<std::endl;
5284 for(u32 i=0; i<count; i++)
5287 MapChunk *chunk = new MapChunk();
5289 is.read((char*)buf, 4);
5292 chunk->deSerialize(is, version);
5293 m_chunks.insert(p, chunk);
5297 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5299 DSTACK(__FUNCTION_NAME);
5300 // Format used for writing
5301 u8 version = SER_FMT_VER_HIGHEST;
5303 v2s16 pos = sector->getPos();
5304 std::string dir = getSectorDir(pos);
5307 std::string fullpath = dir + "/meta";
5308 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5309 if(o.good() == false)
5310 throw FileNotGoodException("Cannot open sector metafile");
5312 sector->serialize(o, version);
5314 sector->differs_from_disk = false;
5317 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5319 DSTACK(__FUNCTION_NAME);
5321 v2s16 p2d = getSectorPos(sectordir);
5323 ServerMapSector *sector = NULL;
5325 std::string fullpath = sectordir + "/meta";
5326 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5327 if(is.good() == false)
5329 // If the directory exists anyway, it probably is in some old
5330 // format. Just go ahead and create the sector.
5331 if(fs::PathExists(sectordir))
5333 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5334 <<fullpath<<" doesn't exist but directory does."
5335 <<" Continuing with a sector with no metadata."
5337 sector = new ServerMapSector(this, p2d);
5338 m_sectors.insert(p2d, sector);
5342 throw FileNotGoodException("Cannot open sector metafile");
5347 sector = ServerMapSector::deSerialize
5348 (is, this, p2d, m_sectors);
5350 saveSectorMeta(sector);
5353 sector->differs_from_disk = false;
5358 bool ServerMap::loadSectorFull(v2s16 p2d)
5360 DSTACK(__FUNCTION_NAME);
5362 MapSector *sector = NULL;
5364 // The directory layout we're going to load from.
5365 // 1 - original sectors/xxxxzzzz/
5366 // 2 - new sectors2/xxx/zzz/
5367 // If we load from anything but the latest structure, we will
5368 // immediately save to the new one, and remove the old.
5370 std::string sectordir1 = getSectorDir(p2d, 1);
5371 std::string sectordir;
5372 if(fs::PathExists(sectordir1))
5374 sectordir = sectordir1;
5379 sectordir = getSectorDir(p2d, 2);
5382 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5385 sector = loadSectorMeta(sectordir, loadlayout != 2);
5387 catch(InvalidFilenameException &e)
5391 catch(FileNotGoodException &e)
5395 catch(std::exception &e)
5403 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5405 std::vector<fs::DirListNode>::iterator i2;
5406 for(i2=list2.begin(); i2!=list2.end(); i2++)
5412 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5414 catch(InvalidFilenameException &e)
5416 // This catches unknown crap in directory
5422 dstream<<"Sector converted to new layout - deleting "<<
5423 sectordir1<<std::endl;
5424 fs::RecursiveDelete(sectordir1);
5431 void ServerMap::saveBlock(MapBlock *block)
5433 DSTACK(__FUNCTION_NAME);
5435 Dummy blocks are not written
5437 if(block->isDummy())
5439 /*v3s16 p = block->getPos();
5440 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5441 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5445 // Format used for writing
5446 u8 version = SER_FMT_VER_HIGHEST;
5448 v3s16 p3d = block->getPos();
5449 v2s16 p2d(p3d.X, p3d.Z);
5450 std::string dir = getSectorDir(p2d);
5454 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5455 std::string fullpath = dir + "/" + cc;
5456 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5457 if(o.good() == false)
5458 throw FileNotGoodException("Cannot open block data");
5461 [0] u8 serialization version
5464 o.write((char*)&version, 1);
5467 block->serialize(o, version);
5469 // Write extra data stored on disk
5470 block->serializeDiskExtra(o, version);
5472 // We just wrote it to the disk so clear modified flag
5473 block->resetChangedFlag();
5476 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5478 DSTACK(__FUNCTION_NAME);
5480 std::string fullpath = sectordir+"/"+blockfile;
5483 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5484 if(is.good() == false)
5485 throw FileNotGoodException("Cannot open block file");
5487 v3s16 p3d = getBlockPos(sectordir, blockfile);
5488 v2s16 p2d(p3d.X, p3d.Z);
5490 assert(sector->getPos() == p2d);
5492 u8 version = SER_FMT_VER_INVALID;
5493 is.read((char*)&version, 1);
5496 throw SerializationError("ServerMap::loadBlock(): Failed"
5497 " to read MapBlock version");
5499 /*u32 block_size = MapBlock::serializedLength(version);
5500 SharedBuffer<u8> data(block_size);
5501 is.read((char*)*data, block_size);*/
5503 // This will always return a sector because we're the server
5504 //MapSector *sector = emergeSector(p2d);
5506 MapBlock *block = NULL;
5507 bool created_new = false;
5509 block = sector->getBlockNoCreate(p3d.Y);
5511 catch(InvalidPositionException &e)
5513 block = sector->createBlankBlockNoInsert(p3d.Y);
5518 block->deSerialize(is, version);
5520 // Read extra data stored on disk
5521 block->deSerializeDiskExtra(is, version);
5523 // If it's a new block, insert it to the map
5525 sector->insertBlock(block);
5528 Save blocks loaded in old format in new format
5531 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5536 // We just loaded it from the disk, so it's up-to-date.
5537 block->resetChangedFlag();
5540 catch(SerializationError &e)
5542 dstream<<"WARNING: Invalid block data on disk "
5543 "(SerializationError). "
5546 //" Ignoring. A new one will be generated.
5549 // TODO: Backup file; name is in fullpath.
5553 void ServerMap::PrintInfo(std::ostream &out)
5564 ClientMap::ClientMap(
5566 MapDrawControl &control,
5567 scene::ISceneNode* parent,
5568 scene::ISceneManager* mgr,
5572 scene::ISceneNode(parent, mgr, id),
5575 m_camera_position(0,0,0),
5576 m_camera_direction(0,0,1)
5578 m_camera_mutex.Init();
5579 assert(m_camera_mutex.IsInitialized());
5581 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5582 BS*1000000,BS*1000000,BS*1000000);
5585 ClientMap::~ClientMap()
5587 /*JMutexAutoLock lock(mesh_mutex);
5596 MapSector * ClientMap::emergeSector(v2s16 p2d)
5598 DSTACK(__FUNCTION_NAME);
5599 // Check that it doesn't exist already
5601 return getSectorNoGenerate(p2d);
5603 catch(InvalidPositionException &e)
5608 ClientMapSector *sector = new ClientMapSector(this, p2d);
5611 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5612 m_sectors.insert(p2d, sector);
5618 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5620 DSTACK(__FUNCTION_NAME);
5621 ClientMapSector *sector = NULL;
5623 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5625 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5629 sector = (ClientMapSector*)n->getValue();
5630 assert(sector->getId() == MAPSECTOR_CLIENT);
5634 sector = new ClientMapSector(this, p2d);
5636 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5637 m_sectors.insert(p2d, sector);
5641 sector->deSerialize(is);
5644 void ClientMap::OnRegisterSceneNode()
5648 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5649 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5652 ISceneNode::OnRegisterSceneNode();
5655 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5657 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5658 DSTACK(__FUNCTION_NAME);
5660 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5663 Get time for measuring timeout.
5665 Measuring time is very useful for long delays when the
5666 machine is swapping a lot.
5668 int time1 = time(0);
5670 //u32 daynight_ratio = m_client->getDayNightRatio();
5672 m_camera_mutex.Lock();
5673 v3f camera_position = m_camera_position;
5674 v3f camera_direction = m_camera_direction;
5675 m_camera_mutex.Unlock();
5678 Get all blocks and draw all visible ones
5681 v3s16 cam_pos_nodes(
5682 camera_position.X / BS,
5683 camera_position.Y / BS,
5684 camera_position.Z / BS);
5686 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5688 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5689 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5691 // Take a fair amount as we will be dropping more out later
5693 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5694 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5695 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5697 p_nodes_max.X / MAP_BLOCKSIZE,
5698 p_nodes_max.Y / MAP_BLOCKSIZE,
5699 p_nodes_max.Z / MAP_BLOCKSIZE);
5701 u32 vertex_count = 0;
5703 // For limiting number of mesh updates per frame
5704 u32 mesh_update_count = 0;
5706 u32 blocks_would_have_drawn = 0;
5707 u32 blocks_drawn = 0;
5709 //NOTE: The sectors map should be locked but we're not doing it
5710 // because it'd cause too much delays
5712 int timecheck_counter = 0;
5713 core::map<v2s16, MapSector*>::Iterator si;
5714 si = m_sectors.getIterator();
5715 for(; si.atEnd() == false; si++)
5718 timecheck_counter++;
5719 if(timecheck_counter > 50)
5721 timecheck_counter = 0;
5722 int time2 = time(0);
5723 if(time2 > time1 + 4)
5725 dstream<<"ClientMap::renderMap(): "
5726 "Rendering takes ages, returning."
5733 MapSector *sector = si.getNode()->getValue();
5734 v2s16 sp = sector->getPos();
5736 if(m_control.range_all == false)
5738 if(sp.X < p_blocks_min.X
5739 || sp.X > p_blocks_max.X
5740 || sp.Y < p_blocks_min.Z
5741 || sp.Y > p_blocks_max.Z)
5745 core::list< MapBlock * > sectorblocks;
5746 sector->getBlocks(sectorblocks);
5752 core::list< MapBlock * >::Iterator i;
5753 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5755 MapBlock *block = *i;
5758 Compare block position to camera position, skip
5759 if not seen on display
5762 float range = 100000 * BS;
5763 if(m_control.range_all == false)
5764 range = m_control.wanted_range * BS;
5767 if(isBlockInSight(block->getPos(), camera_position,
5768 camera_direction, range, &d) == false)
5773 // This is ugly (spherical distance limit?)
5774 /*if(m_control.range_all == false &&
5775 d - 0.5*BS*MAP_BLOCKSIZE > range)
5780 Update expired mesh (used for day/night change)
5782 It doesn't work exactly like it should now with the
5783 tasked mesh update but whatever.
5786 bool mesh_expired = false;
5789 JMutexAutoLock lock(block->mesh_mutex);
5791 mesh_expired = block->getMeshExpired();
5793 // Mesh has not been expired and there is no mesh:
5794 // block has no content
5795 if(block->mesh == NULL && mesh_expired == false)
5799 f32 faraway = BS*50;
5800 //f32 faraway = m_control.wanted_range * BS;
5803 This has to be done with the mesh_mutex unlocked
5805 // Pretty random but this should work somewhat nicely
5806 if(mesh_expired && (
5807 (mesh_update_count < 3
5808 && (d < faraway || mesh_update_count < 2)
5811 (m_control.range_all && mesh_update_count < 20)
5814 /*if(mesh_expired && mesh_update_count < 6
5815 && (d < faraway || mesh_update_count < 3))*/
5817 mesh_update_count++;
5819 // Mesh has been expired: generate new mesh
5820 //block->updateMesh(daynight_ratio);
5821 m_client->addUpdateMeshTask(block->getPos());
5823 mesh_expired = false;
5828 Draw the faces of the block
5831 JMutexAutoLock lock(block->mesh_mutex);
5833 scene::SMesh *mesh = block->mesh;
5838 blocks_would_have_drawn++;
5839 if(blocks_drawn >= m_control.wanted_max_blocks
5840 && m_control.range_all == false
5841 && d > m_control.wanted_min_range * BS)
5845 u32 c = mesh->getMeshBufferCount();
5847 for(u32 i=0; i<c; i++)
5849 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5850 const video::SMaterial& material = buf->getMaterial();
5851 video::IMaterialRenderer* rnd =
5852 driver->getMaterialRenderer(material.MaterialType);
5853 bool transparent = (rnd && rnd->isTransparent());
5854 // Render transparent on transparent pass and likewise.
5855 if(transparent == is_transparent_pass)
5858 This *shouldn't* hurt too much because Irrlicht
5859 doesn't change opengl textures if the old
5860 material is set again.
5862 driver->setMaterial(buf->getMaterial());
5863 driver->drawMeshBuffer(buf);
5864 vertex_count += buf->getVertexCount();
5868 } // foreach sectorblocks
5871 m_control.blocks_drawn = blocks_drawn;
5872 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5874 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5875 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5878 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5879 core::map<v3s16, MapBlock*> *affected_blocks)
5881 bool changed = false;
5883 Add it to all blocks touching it
5886 v3s16(0,0,0), // this
5887 v3s16(0,0,1), // back
5888 v3s16(0,1,0), // top
5889 v3s16(1,0,0), // right
5890 v3s16(0,0,-1), // front
5891 v3s16(0,-1,0), // bottom
5892 v3s16(-1,0,0), // left
5894 for(u16 i=0; i<7; i++)
5896 v3s16 p2 = p + dirs[i];
5897 // Block position of neighbor (or requested) node
5898 v3s16 blockpos = getNodeBlockPos(p2);
5899 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5900 if(blockref == NULL)
5902 // Relative position of requested node
5903 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5904 if(blockref->setTempMod(relpos, mod))
5909 if(changed && affected_blocks!=NULL)
5911 for(u16 i=0; i<7; i++)
5913 v3s16 p2 = p + dirs[i];
5914 // Block position of neighbor (or requested) node
5915 v3s16 blockpos = getNodeBlockPos(p2);
5916 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5917 if(blockref == NULL)
5919 affected_blocks->insert(blockpos, blockref);
5925 bool ClientMap::clearTempMod(v3s16 p,
5926 core::map<v3s16, MapBlock*> *affected_blocks)
5928 bool changed = false;
5930 v3s16(0,0,0), // this
5931 v3s16(0,0,1), // back
5932 v3s16(0,1,0), // top
5933 v3s16(1,0,0), // right
5934 v3s16(0,0,-1), // front
5935 v3s16(0,-1,0), // bottom
5936 v3s16(-1,0,0), // left
5938 for(u16 i=0; i<7; i++)
5940 v3s16 p2 = p + dirs[i];
5941 // Block position of neighbor (or requested) node
5942 v3s16 blockpos = getNodeBlockPos(p2);
5943 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5944 if(blockref == NULL)
5946 // Relative position of requested node
5947 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5948 if(blockref->clearTempMod(relpos))
5953 if(changed && affected_blocks!=NULL)
5955 for(u16 i=0; i<7; i++)
5957 v3s16 p2 = p + dirs[i];
5958 // Block position of neighbor (or requested) node
5959 v3s16 blockpos = getNodeBlockPos(p2);
5960 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5961 if(blockref == NULL)
5963 affected_blocks->insert(blockpos, blockref);
5969 void ClientMap::expireMeshes(bool only_daynight_diffed)
5971 TimeTaker timer("expireMeshes()");
5973 core::map<v2s16, MapSector*>::Iterator si;
5974 si = m_sectors.getIterator();
5975 for(; si.atEnd() == false; si++)
5977 MapSector *sector = si.getNode()->getValue();
5979 core::list< MapBlock * > sectorblocks;
5980 sector->getBlocks(sectorblocks);
5982 core::list< MapBlock * >::Iterator i;
5983 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5985 MapBlock *block = *i;
5987 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5993 JMutexAutoLock lock(block->mesh_mutex);
5994 if(block->mesh != NULL)
5996 /*block->mesh->drop();
5997 block->mesh = NULL;*/
5998 block->setMeshExpired(true);
6005 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6007 assert(mapType() == MAPTYPE_CLIENT);
6010 v3s16 p = blockpos + v3s16(0,0,0);
6011 MapBlock *b = getBlockNoCreate(p);
6012 b->updateMesh(daynight_ratio);
6013 //b->setMeshExpired(true);
6015 catch(InvalidPositionException &e){}
6018 v3s16 p = blockpos + v3s16(-1,0,0);
6019 MapBlock *b = getBlockNoCreate(p);
6020 b->updateMesh(daynight_ratio);
6021 //b->setMeshExpired(true);
6023 catch(InvalidPositionException &e){}
6025 v3s16 p = blockpos + v3s16(0,-1,0);
6026 MapBlock *b = getBlockNoCreate(p);
6027 b->updateMesh(daynight_ratio);
6028 //b->setMeshExpired(true);
6030 catch(InvalidPositionException &e){}
6032 v3s16 p = blockpos + v3s16(0,0,-1);
6033 MapBlock *b = getBlockNoCreate(p);
6034 b->updateMesh(daynight_ratio);
6035 //b->setMeshExpired(true);
6037 catch(InvalidPositionException &e){}
6042 Update mesh of block in which the node is, and if the node is at the
6043 leading edge, update the appropriate leading blocks too.
6045 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6053 v3s16 blockposes[4];
6054 for(u32 i=0; i<4; i++)
6056 v3s16 np = nodepos + dirs[i];
6057 blockposes[i] = getNodeBlockPos(np);
6058 // Don't update mesh of block if it has been done already
6059 bool already_updated = false;
6060 for(u32 j=0; j<i; j++)
6062 if(blockposes[j] == blockposes[i])
6064 already_updated = true;
6071 MapBlock *b = getBlockNoCreate(blockposes[i]);
6072 b->updateMesh(daynight_ratio);
6077 void ClientMap::PrintInfo(std::ostream &out)
6088 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6093 MapVoxelManipulator::~MapVoxelManipulator()
6095 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6099 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6101 TimeTaker timer1("emerge", &emerge_time);
6103 // Units of these are MapBlocks
6104 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6105 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6107 VoxelArea block_area_nodes
6108 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6110 addArea(block_area_nodes);
6112 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6113 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6114 for(s32 x=p_min.X; x<=p_max.X; x++)
6117 core::map<v3s16, bool>::Node *n;
6118 n = m_loaded_blocks.find(p);
6122 bool block_data_inexistent = false;
6125 TimeTaker timer1("emerge load", &emerge_load_time);
6127 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6128 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6131 dstream<<std::endl;*/
6133 MapBlock *block = m_map->getBlockNoCreate(p);
6134 if(block->isDummy())
6135 block_data_inexistent = true;
6137 block->copyTo(*this);
6139 catch(InvalidPositionException &e)
6141 block_data_inexistent = true;
6144 if(block_data_inexistent)
6146 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6147 // Fill with VOXELFLAG_INEXISTENT
6148 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6149 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6151 s32 i = m_area.index(a.MinEdge.X,y,z);
6152 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6156 m_loaded_blocks.insert(p, !block_data_inexistent);
6159 //dstream<<"emerge done"<<std::endl;
6163 SUGG: Add an option to only update eg. water and air nodes.
6164 This will make it interfere less with important stuff if
6167 void MapVoxelManipulator::blitBack
6168 (core::map<v3s16, MapBlock*> & modified_blocks)
6170 if(m_area.getExtent() == v3s16(0,0,0))
6173 //TimeTaker timer1("blitBack");
6175 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6176 <<m_loaded_blocks.size()<<std::endl;*/
6179 Initialize block cache
6181 v3s16 blockpos_last;
6182 MapBlock *block = NULL;
6183 bool block_checked_in_modified = false;
6185 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6186 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6187 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6191 u8 f = m_flags[m_area.index(p)];
6192 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6195 MapNode &n = m_data[m_area.index(p)];
6197 v3s16 blockpos = getNodeBlockPos(p);
6202 if(block == NULL || blockpos != blockpos_last){
6203 block = m_map->getBlockNoCreate(blockpos);
6204 blockpos_last = blockpos;
6205 block_checked_in_modified = false;
6208 // Calculate relative position in block
6209 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6211 // Don't continue if nothing has changed here
6212 if(block->getNode(relpos) == n)
6215 //m_map->setNode(m_area.MinEdge + p, n);
6216 block->setNode(relpos, n);
6219 Make sure block is in modified_blocks
6221 if(block_checked_in_modified == false)
6223 modified_blocks[blockpos] = block;
6224 block_checked_in_modified = true;
6227 catch(InvalidPositionException &e)
6233 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6234 MapVoxelManipulator(map),
6235 m_create_area(false)
6239 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6243 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6245 // Just create the area so that it can be pointed to
6246 VoxelManipulator::emerge(a, caller_id);
6249 void ManualMapVoxelManipulator::initialEmerge(
6250 v3s16 blockpos_min, v3s16 blockpos_max)
6252 TimeTaker timer1("initialEmerge", &emerge_time);
6254 // Units of these are MapBlocks
6255 v3s16 p_min = blockpos_min;
6256 v3s16 p_max = blockpos_max;
6258 VoxelArea block_area_nodes
6259 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6261 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6264 dstream<<"initialEmerge: area: ";
6265 block_area_nodes.print(dstream);
6266 dstream<<" ("<<size_MB<<"MB)";
6270 addArea(block_area_nodes);
6272 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6273 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6274 for(s32 x=p_min.X; x<=p_max.X; x++)
6277 core::map<v3s16, bool>::Node *n;
6278 n = m_loaded_blocks.find(p);
6282 bool block_data_inexistent = false;
6285 TimeTaker timer1("emerge load", &emerge_load_time);
6287 MapBlock *block = m_map->getBlockNoCreate(p);
6288 if(block->isDummy())
6289 block_data_inexistent = true;
6291 block->copyTo(*this);
6293 catch(InvalidPositionException &e)
6295 block_data_inexistent = true;
6298 if(block_data_inexistent)
6301 Mark area inexistent
6303 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6304 // Fill with VOXELFLAG_INEXISTENT
6305 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6306 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6308 s32 i = m_area.index(a.MinEdge.X,y,z);
6309 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6313 m_loaded_blocks.insert(p, !block_data_inexistent);
6317 void ManualMapVoxelManipulator::blitBackAll(
6318 core::map<v3s16, MapBlock*> * modified_blocks)
6320 if(m_area.getExtent() == v3s16(0,0,0))
6324 Copy data of all blocks
6326 for(core::map<v3s16, bool>::Iterator
6327 i = m_loaded_blocks.getIterator();
6328 i.atEnd() == false; i++)
6330 bool existed = i.getNode()->getValue();
6331 if(existed == false)
6333 v3s16 p = i.getNode()->getKey();
6334 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6337 dstream<<"WARNING: "<<__FUNCTION_NAME
6338 <<": got NULL block "
6339 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6344 block->copyFrom(*this);
6347 modified_blocks->insert(p, block);