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();
841 This is called after changing a node from transparent to opaque.
842 The lighting value of the node should be left as-is after changing
843 other values. This sets the lighting value to 0.
845 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
846 core::map<v3s16, MapBlock*> &modified_blocks)
849 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
850 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
853 From this node to nodes underneath:
854 If lighting is sunlight (1.0), unlight neighbours and
859 v3s16 toppos = p + v3s16(0,1,0);
860 v3s16 bottompos = p + v3s16(0,-1,0);
862 bool node_under_sunlight = true;
863 core::map<v3s16, bool> light_sources;
866 If there is a node at top and it doesn't have sunlight,
867 there has not been any sunlight going down.
869 Otherwise there probably is.
872 MapNode topnode = getNode(toppos);
874 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
875 node_under_sunlight = false;
877 catch(InvalidPositionException &e)
882 If the new node doesn't propagate sunlight and there is
883 grass below, change it to mud
885 if(content_features(n.d).sunlight_propagates == false)
888 MapNode bottomnode = getNode(bottompos);
890 if(bottomnode.d == CONTENT_GRASS
891 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
893 bottomnode.d = CONTENT_MUD;
894 setNode(bottompos, bottomnode);
897 catch(InvalidPositionException &e)
903 If the new node is mud and it is under sunlight, change it
906 if(n.d == CONTENT_MUD && node_under_sunlight)
912 Remove all light that has come out of this node
915 enum LightBank banks[] =
920 for(s32 i=0; i<2; i++)
922 enum LightBank bank = banks[i];
924 u8 lightwas = getNode(p).getLight(bank);
926 // Add the block of the added node to modified_blocks
927 v3s16 blockpos = getNodeBlockPos(p);
928 MapBlock * block = getBlockNoCreate(blockpos);
929 assert(block != NULL);
930 modified_blocks.insert(blockpos, block);
932 assert(isValidPosition(p));
934 // Unlight neighbours of node.
935 // This means setting light of all consequent dimmer nodes
937 // This also collects the nodes at the border which will spread
938 // light again into this.
939 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
945 If node lets sunlight through and is under sunlight, it has
948 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
950 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
954 Set the node on the map
963 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
966 NodeMetadata *meta = meta_proto->clone();
967 setNodeMetadata(p, meta);
971 If node is under sunlight and doesn't let sunlight through,
972 take all sunlighted nodes under it and clear light from them
973 and from where the light has been spread.
974 TODO: This could be optimized by mass-unlighting instead
977 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
981 //m_dout<<DTIME<<"y="<<y<<std::endl;
982 v3s16 n2pos(p.X, y, p.Z);
988 catch(InvalidPositionException &e)
993 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
995 unLightNeighbors(LIGHTBANK_DAY,
996 n2pos, n2.getLight(LIGHTBANK_DAY),
997 light_sources, modified_blocks);
998 n2.setLight(LIGHTBANK_DAY, 0);
1006 for(s32 i=0; i<2; i++)
1008 enum LightBank bank = banks[i];
1011 Spread light from all nodes that might be capable of doing so
1013 spreadLight(bank, light_sources, modified_blocks);
1017 Update information about whether day and night light differ
1019 for(core::map<v3s16, MapBlock*>::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 MapBlock *block = i.getNode()->getValue();
1024 block->updateDayNightDiff();
1028 Add neighboring liquid nodes and the node itself if it is
1029 liquid (=water node was added) to transform queue.
1032 v3s16(0,0,0), // self
1033 v3s16(0,0,1), // back
1034 v3s16(0,1,0), // top
1035 v3s16(1,0,0), // right
1036 v3s16(0,0,-1), // front
1037 v3s16(0,-1,0), // bottom
1038 v3s16(-1,0,0), // left
1040 for(u16 i=0; i<7; i++)
1045 v3s16 p2 = p + dirs[i];
1047 MapNode n2 = getNode(p2);
1048 if(content_liquid(n2.d))
1050 m_transforming_liquid.push_back(p2);
1053 }catch(InvalidPositionException &e)
1061 void Map::removeNodeAndUpdate(v3s16 p,
1062 core::map<v3s16, MapBlock*> &modified_blocks)
1064 /*PrintInfo(m_dout);
1065 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1066 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1068 bool node_under_sunlight = true;
1070 v3s16 toppos = p + v3s16(0,1,0);
1072 // Node will be replaced with this
1073 u8 replace_material = CONTENT_AIR;
1076 If there is a node at top and it doesn't have sunlight,
1077 there will be no sunlight going down.
1080 MapNode topnode = getNode(toppos);
1082 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1083 node_under_sunlight = false;
1085 catch(InvalidPositionException &e)
1089 core::map<v3s16, bool> light_sources;
1091 enum LightBank banks[] =
1096 for(s32 i=0; i<2; i++)
1098 enum LightBank bank = banks[i];
1101 Unlight neighbors (in case the node is a light source)
1103 unLightNeighbors(bank, p,
1104 getNode(p).getLight(bank),
1105 light_sources, modified_blocks);
1109 Remove node metadata
1112 removeNodeMetadata(p);
1116 This also clears the lighting.
1120 n.d = replace_material;
1123 for(s32 i=0; i<2; i++)
1125 enum LightBank bank = banks[i];
1128 Recalculate lighting
1130 spreadLight(bank, light_sources, modified_blocks);
1133 // Add the block of the removed node to modified_blocks
1134 v3s16 blockpos = getNodeBlockPos(p);
1135 MapBlock * block = getBlockNoCreate(blockpos);
1136 assert(block != NULL);
1137 modified_blocks.insert(blockpos, block);
1140 If the removed node was under sunlight, propagate the
1141 sunlight down from it and then light all neighbors
1142 of the propagated blocks.
1144 if(node_under_sunlight)
1146 s16 ybottom = propagateSunlight(p, modified_blocks);
1147 /*m_dout<<DTIME<<"Node was under sunlight. "
1148 "Propagating sunlight";
1149 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1151 for(; y >= ybottom; y--)
1153 v3s16 p2(p.X, y, p.Z);
1154 /*m_dout<<DTIME<<"lighting neighbors of node ("
1155 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1157 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1162 // Set the lighting of this node to 0
1163 // TODO: Is this needed? Lighting is cleared up there already.
1165 MapNode n = getNode(p);
1166 n.setLight(LIGHTBANK_DAY, 0);
1169 catch(InvalidPositionException &e)
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1179 // Get the brightest neighbour node and propagate light from it
1180 v3s16 n2p = getBrightestNeighbour(bank, p);
1182 MapNode n2 = getNode(n2p);
1183 lightNeighbors(bank, n2p, modified_blocks);
1185 catch(InvalidPositionException &e)
1191 Update information about whether day and night light differ
1193 for(core::map<v3s16, MapBlock*>::Iterator
1194 i = modified_blocks.getIterator();
1195 i.atEnd() == false; i++)
1197 MapBlock *block = i.getNode()->getValue();
1198 block->updateDayNightDiff();
1202 Add neighboring liquid nodes to transform queue.
1205 v3s16(0,0,1), // back
1206 v3s16(0,1,0), // top
1207 v3s16(1,0,0), // right
1208 v3s16(0,0,-1), // front
1209 v3s16(0,-1,0), // bottom
1210 v3s16(-1,0,0), // left
1212 for(u16 i=0; i<6; i++)
1217 v3s16 p2 = p + dirs[i];
1219 MapNode n2 = getNode(p2);
1220 if(content_liquid(n2.d))
1222 m_transforming_liquid.push_back(p2);
1225 }catch(InvalidPositionException &e)
1231 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1234 event.type = MEET_ADDNODE;
1238 bool succeeded = true;
1240 core::map<v3s16, MapBlock*> modified_blocks;
1241 addNodeAndUpdate(p, n, modified_blocks);
1243 // Copy modified_blocks to event
1244 for(core::map<v3s16, MapBlock*>::Iterator
1245 i = modified_blocks.getIterator();
1246 i.atEnd()==false; i++)
1248 event.modified_blocks.insert(i.getNode()->getKey(), false);
1251 catch(InvalidPositionException &e){
1255 dispatchEvent(&event);
1260 bool Map::removeNodeWithEvent(v3s16 p)
1263 event.type = MEET_REMOVENODE;
1266 bool succeeded = true;
1268 core::map<v3s16, MapBlock*> modified_blocks;
1269 removeNodeAndUpdate(p, modified_blocks);
1271 // Copy modified_blocks to event
1272 for(core::map<v3s16, MapBlock*>::Iterator
1273 i = modified_blocks.getIterator();
1274 i.atEnd()==false; i++)
1276 event.modified_blocks.insert(i.getNode()->getKey(), false);
1279 catch(InvalidPositionException &e){
1283 dispatchEvent(&event);
1288 bool Map::dayNightDiffed(v3s16 blockpos)
1291 v3s16 p = blockpos + v3s16(0,0,0);
1292 MapBlock *b = getBlockNoCreate(p);
1293 if(b->dayNightDiffed())
1296 catch(InvalidPositionException &e){}
1299 v3s16 p = blockpos + v3s16(-1,0,0);
1300 MapBlock *b = getBlockNoCreate(p);
1301 if(b->dayNightDiffed())
1304 catch(InvalidPositionException &e){}
1306 v3s16 p = blockpos + v3s16(0,-1,0);
1307 MapBlock *b = getBlockNoCreate(p);
1308 if(b->dayNightDiffed())
1311 catch(InvalidPositionException &e){}
1313 v3s16 p = blockpos + v3s16(0,0,-1);
1314 MapBlock *b = getBlockNoCreate(p);
1315 if(b->dayNightDiffed())
1318 catch(InvalidPositionException &e){}
1321 v3s16 p = blockpos + v3s16(1,0,0);
1322 MapBlock *b = getBlockNoCreate(p);
1323 if(b->dayNightDiffed())
1326 catch(InvalidPositionException &e){}
1328 v3s16 p = blockpos + v3s16(0,1,0);
1329 MapBlock *b = getBlockNoCreate(p);
1330 if(b->dayNightDiffed())
1333 catch(InvalidPositionException &e){}
1335 v3s16 p = blockpos + v3s16(0,0,1);
1336 MapBlock *b = getBlockNoCreate(p);
1337 if(b->dayNightDiffed())
1340 catch(InvalidPositionException &e){}
1346 Updates usage timers
1348 void Map::timerUpdate(float dtime)
1350 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1352 core::map<v2s16, MapSector*>::Iterator si;
1354 si = m_sectors.getIterator();
1355 for(; si.atEnd() == false; si++)
1357 MapSector *sector = si.getNode()->getValue();
1358 sector->usage_timer += dtime;
1362 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1365 Wait for caches to be removed before continuing.
1367 This disables the existence of caches while locked
1369 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1371 core::list<v2s16>::Iterator j;
1372 for(j=list.begin(); j!=list.end(); j++)
1374 MapSector *sector = m_sectors[*j];
1377 sector->deleteBlocks();
1382 If sector is in sector cache, remove it from there
1384 if(m_sector_cache == sector)
1386 m_sector_cache = NULL;
1389 Remove from map and delete
1391 m_sectors.remove(*j);
1397 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1398 core::list<v3s16> *deleted_blocks)
1400 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1402 core::list<v2s16> sector_deletion_queue;
1403 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1404 for(; i.atEnd() == false; i++)
1406 MapSector *sector = i.getNode()->getValue();
1408 Delete sector from memory if it hasn't been used in a long time
1410 if(sector->usage_timer > timeout)
1412 sector_deletion_queue.push_back(i.getNode()->getKey());
1414 if(deleted_blocks != NULL)
1416 // Collect positions of blocks of sector
1417 MapSector *sector = i.getNode()->getValue();
1418 core::list<MapBlock*> blocks;
1419 sector->getBlocks(blocks);
1420 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1421 i != blocks.end(); i++)
1423 deleted_blocks->push_back((*i)->getPos());
1428 deleteSectors(sector_deletion_queue, only_blocks);
1429 return sector_deletion_queue.getSize();
1432 void Map::PrintInfo(std::ostream &out)
1437 #define WATER_DROP_BOOST 4
1439 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1441 DSTACK(__FUNCTION_NAME);
1442 //TimeTaker timer("transformLiquids()");
1445 u32 initial_size = m_transforming_liquid.size();
1447 /*if(initial_size != 0)
1448 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1450 while(m_transforming_liquid.size() != 0)
1453 Get a queued transforming liquid node
1455 v3s16 p0 = m_transforming_liquid.pop_front();
1457 MapNode n0 = getNode(p0);
1459 // Don't deal with non-liquids
1460 if(content_liquid(n0.d) == false)
1463 bool is_source = !content_flowing_liquid(n0.d);
1465 u8 liquid_level = 8;
1466 if(is_source == false)
1467 liquid_level = n0.param2 & 0x0f;
1469 // Turn possible source into non-source
1470 u8 nonsource_c = make_liquid_flowing(n0.d);
1473 If not source, check that some node flows into this one
1474 and what is the level of liquid in this one
1476 if(is_source == false)
1478 s8 new_liquid_level_max = -1;
1480 v3s16 dirs_from[5] = {
1481 v3s16(0,1,0), // top
1482 v3s16(0,0,1), // back
1483 v3s16(1,0,0), // right
1484 v3s16(0,0,-1), // front
1485 v3s16(-1,0,0), // left
1487 for(u16 i=0; i<5; i++)
1492 bool from_top = (i==0);
1494 v3s16 p2 = p0 + dirs_from[i];
1495 MapNode n2 = getNode(p2);
1497 if(content_liquid(n2.d))
1499 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1500 // Check that the liquids are the same type
1501 if(n2_nonsource_c != nonsource_c)
1503 dstream<<"WARNING: Not handling: different liquids"
1504 " collide"<<std::endl;
1507 bool n2_is_source = !content_flowing_liquid(n2.d);
1508 s8 n2_liquid_level = 8;
1509 if(n2_is_source == false)
1510 n2_liquid_level = n2.param2 & 0x07;
1512 s8 new_liquid_level = -1;
1515 //new_liquid_level = 7;
1516 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1517 new_liquid_level = 7;
1519 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1521 else if(n2_liquid_level > 0)
1523 new_liquid_level = n2_liquid_level - 1;
1526 if(new_liquid_level > new_liquid_level_max)
1527 new_liquid_level_max = new_liquid_level;
1530 }catch(InvalidPositionException &e)
1536 If liquid level should be something else, update it and
1537 add all the neighboring water nodes to the transform queue.
1539 if(new_liquid_level_max != liquid_level)
1541 if(new_liquid_level_max == -1)
1543 // Remove water alltoghether
1550 n0.param2 = new_liquid_level_max;
1554 // Block has been modified
1556 v3s16 blockpos = getNodeBlockPos(p0);
1557 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1559 modified_blocks.insert(blockpos, block);
1563 Add neighboring non-source liquid nodes to transform queue.
1566 v3s16(0,0,1), // back
1567 v3s16(0,1,0), // top
1568 v3s16(1,0,0), // right
1569 v3s16(0,0,-1), // front
1570 v3s16(0,-1,0), // bottom
1571 v3s16(-1,0,0), // left
1573 for(u16 i=0; i<6; i++)
1578 v3s16 p2 = p0 + dirs[i];
1580 MapNode n2 = getNode(p2);
1581 if(content_flowing_liquid(n2.d))
1583 m_transforming_liquid.push_back(p2);
1586 }catch(InvalidPositionException &e)
1593 // Get a new one from queue if the node has turned into non-water
1594 if(content_liquid(n0.d) == false)
1598 Flow water from this node
1600 v3s16 dirs_to[5] = {
1601 v3s16(0,-1,0), // bottom
1602 v3s16(0,0,1), // back
1603 v3s16(1,0,0), // right
1604 v3s16(0,0,-1), // front
1605 v3s16(-1,0,0), // left
1607 for(u16 i=0; i<5; i++)
1612 bool to_bottom = (i == 0);
1614 // If liquid is at lowest possible height, it's not going
1615 // anywhere except down
1616 if(liquid_level == 0 && to_bottom == false)
1619 u8 liquid_next_level = 0;
1620 // If going to bottom
1623 //liquid_next_level = 7;
1624 if(liquid_level >= 7 - WATER_DROP_BOOST)
1625 liquid_next_level = 7;
1627 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1630 liquid_next_level = liquid_level - 1;
1632 bool n2_changed = false;
1633 bool flowed = false;
1635 v3s16 p2 = p0 + dirs_to[i];
1637 MapNode n2 = getNode(p2);
1638 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1640 if(content_liquid(n2.d))
1642 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1643 // Check that the liquids are the same type
1644 if(n2_nonsource_c != nonsource_c)
1646 dstream<<"WARNING: Not handling: different liquids"
1647 " collide"<<std::endl;
1650 bool n2_is_source = !content_flowing_liquid(n2.d);
1651 u8 n2_liquid_level = 8;
1652 if(n2_is_source == false)
1653 n2_liquid_level = n2.param2 & 0x07;
1662 // Just flow into the source, nothing changes.
1663 // n2_changed is not set because destination didn't change
1668 if(liquid_next_level > liquid_level)
1670 n2.param2 = liquid_next_level;
1678 else if(n2.d == CONTENT_AIR)
1681 n2.param2 = liquid_next_level;
1688 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1692 m_transforming_liquid.push_back(p2);
1694 v3s16 blockpos = getNodeBlockPos(p2);
1695 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1697 modified_blocks.insert(blockpos, block);
1700 // If n2_changed to bottom, don't flow anywhere else
1701 if(to_bottom && flowed && !is_source)
1704 }catch(InvalidPositionException &e)
1710 //if(loopcount >= 100000)
1711 if(loopcount >= initial_size * 1)
1714 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1717 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1719 v3s16 blockpos = getNodeBlockPos(p);
1720 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1721 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1724 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1728 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1732 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1734 v3s16 blockpos = getNodeBlockPos(p);
1735 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1736 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1739 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1743 block->m_node_metadata.set(p_rel, meta);
1746 void Map::removeNodeMetadata(v3s16 p)
1748 v3s16 blockpos = getNodeBlockPos(p);
1749 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1750 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1753 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1757 block->m_node_metadata.remove(p_rel);
1760 void Map::nodeMetadataStep(float dtime,
1761 core::map<v3s16, MapBlock*> &changed_blocks)
1765 Currently there is no way to ensure that all the necessary
1766 blocks are loaded when this is run. (They might get unloaded)
1767 NOTE: ^- Actually, that might not be so. In a quick test it
1768 reloaded a block with a furnace when I walked back to it from
1771 core::map<v2s16, MapSector*>::Iterator si;
1772 si = m_sectors.getIterator();
1773 for(; si.atEnd() == false; si++)
1775 MapSector *sector = si.getNode()->getValue();
1776 core::list< MapBlock * > sectorblocks;
1777 sector->getBlocks(sectorblocks);
1778 core::list< MapBlock * >::Iterator i;
1779 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1781 MapBlock *block = *i;
1782 bool changed = block->m_node_metadata.step(dtime);
1784 changed_blocks[block->getPos()] = block;
1793 ServerMap::ServerMap(std::string savedir):
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 data.chunkpos = chunkpos;
3592 data.y_blocks_min = y_blocks_min;
3593 data.y_blocks_max = y_blocks_max;
3594 data.sectorpos_base = sectorpos_base;
3595 data.sectorpos_base_size = sectorpos_base_size;
3596 data.sectorpos_bigbase = sectorpos_bigbase;
3597 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3598 data.max_spread_amount = max_spread_amount;
3601 Create the whole area of this and the neighboring chunks
3604 TimeTaker timer("initChunkMake() create area");
3606 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3607 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3609 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3610 ServerMapSector *sector = createSector(sectorpos);
3613 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3615 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3616 MapBlock *block = createBlock(blockpos);
3618 // Lighting won't be calculated
3619 //block->setLightingExpired(true);
3620 // Lighting will be calculated
3621 block->setLightingExpired(false);
3624 Block gets sunlight if this is true.
3626 This should be set to true when the top side of a block
3627 is completely exposed to the sky.
3629 Actually this doesn't matter now because the
3630 initial lighting is done here.
3632 block->setIsUnderground(y != y_blocks_max);
3638 Now we have a big empty area.
3640 Make a ManualMapVoxelManipulator that contains this and the
3644 v3s16 bigarea_blocks_min(
3645 sectorpos_bigbase.X,
3649 v3s16 bigarea_blocks_max(
3650 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3652 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3655 data.vmanip.setMap(this);
3658 TimeTaker timer("initChunkMake() initialEmerge");
3659 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3664 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3665 core::map<v3s16, MapBlock*> &changed_blocks)
3671 Blit generated stuff to map
3675 //TimeTaker timer("generateChunkRaw() blitBackAll");
3676 data.vmanip.blitBackAll(&changed_blocks);
3680 Update day/night difference cache of the MapBlocks
3683 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3684 i.atEnd() == false; i++)
3686 MapBlock *block = i.getNode()->getValue();
3687 block->updateDayNightDiff();
3692 Copy transforming liquid information
3694 while(data.transforming_liquid.size() > 0)
3696 v3s16 p = data.transforming_liquid.pop_front();
3697 m_transforming_liquid.push_back(p);
3701 Add random objects to blocks
3704 for(s16 x=0; x<data.sectorpos_base_size; x++)
3705 for(s16 z=0; z<data.sectorpos_base_size; z++)
3707 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3708 ServerMapSector *sector = createSector(sectorpos);
3711 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3713 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3714 MapBlock *block = createBlock(blockpos);
3715 addRandomObjects(block);
3721 Create chunk metadata
3724 for(s16 x=-1; x<=1; x++)
3725 for(s16 y=-1; y<=1; y++)
3727 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3728 // Add chunk meta information
3729 MapChunk *chunk = getChunk(chunkpos0);
3732 chunk = new MapChunk();
3733 m_chunks.insert(chunkpos0, chunk);
3735 //chunk->setIsVolatile(true);
3736 if(chunk->getGenLevel() > GENERATED_PARTLY)
3737 chunk->setGenLevel(GENERATED_PARTLY);
3741 Set central chunk non-volatile
3743 MapChunk *chunk = getChunk(data.chunkpos);
3746 //chunk->setIsVolatile(false);
3747 chunk->setGenLevel(GENERATED_FULLY);
3750 Save changed parts of map
3759 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3760 core::map<v3s16, MapBlock*> &changed_blocks,
3763 DSTACK(__FUNCTION_NAME);
3766 Don't generate if already fully generated
3770 MapChunk *chunk = getChunk(chunkpos);
3771 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3773 dstream<<"generateChunkRaw(): Chunk "
3774 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3775 <<" already generated"<<std::endl;
3780 dstream<<"generateChunkRaw(): Generating chunk "
3781 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3784 TimeTaker timer("generateChunkRaw()");
3788 // Initialize generation
3789 initChunkMake(data, chunkpos);
3794 // Finalize generation
3795 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3798 Return central chunk (which was requested)
3804 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3805 core::map<v3s16, MapBlock*> &changed_blocks)
3807 dstream<<"generateChunk(): Generating chunk "
3808 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3811 /*for(s16 x=-1; x<=1; x++)
3812 for(s16 y=-1; y<=1; y++)*/
3813 for(s16 x=-0; x<=0; x++)
3814 for(s16 y=-0; y<=0; y++)
3816 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3817 MapChunk *chunk = getChunk(chunkpos0);
3818 // Skip if already generated
3819 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3821 generateChunkRaw(chunkpos0, changed_blocks);
3824 assert(chunkNonVolatile(chunkpos1));
3826 MapChunk *chunk = getChunk(chunkpos1);
3831 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3833 DSTACKF("%s: p2d=(%d,%d)",
3838 Check if it exists already in memory
3840 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3845 Try to load it from disk (with blocks)
3847 if(loadSectorFull(p2d) == true)
3849 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3852 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3853 throw InvalidPositionException("");
3859 Do not create over-limit
3861 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3862 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3863 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3864 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3865 throw InvalidPositionException("createSector(): pos. over limit");
3868 Generate blank sector
3871 sector = new ServerMapSector(this, p2d);
3873 // Sector position on map in nodes
3874 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3879 m_sectors.insert(p2d, sector);
3885 MapSector * ServerMap::emergeSector(v2s16 p2d,
3886 core::map<v3s16, MapBlock*> &changed_blocks)
3888 DSTACK("%s: p2d=(%d,%d)",
3895 v2s16 chunkpos = sector_to_chunk(p2d);
3896 /*bool chunk_nonvolatile = false;
3897 MapChunk *chunk = getChunk(chunkpos);
3898 if(chunk && chunk->getIsVolatile() == false)
3899 chunk_nonvolatile = true;*/
3900 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3903 If chunk is not fully generated, generate chunk
3905 if(chunk_nonvolatile == false)
3907 // Generate chunk and neighbors
3908 generateChunk(chunkpos, changed_blocks);
3912 Return sector if it exists now
3914 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3919 Try to load it from disk
3921 if(loadSectorFull(p2d) == true)
3923 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3926 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3927 throw InvalidPositionException("");
3933 generateChunk should have generated the sector
3937 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3938 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3942 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3944 return createSector(p2d);
3949 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3952 generateChunkRaw(chunkpos, changed_blocks, true);
3955 Return sector if it exists now
3957 sector = getSectorNoGenerateNoEx(p2d);
3961 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3969 //return generateSector();
3974 NOTE: This is not used for main map generation, only for blocks
3975 that are very high or low
3977 MapBlock * ServerMap::generateBlock(
3979 MapBlock *original_dummy,
3980 ServerMapSector *sector,
3981 core::map<v3s16, MapBlock*> &changed_blocks,
3982 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3985 DSTACKF("%s: p=(%d,%d,%d)",
3989 // If chunks are disabled
3990 /*if(m_chunksize == 0)
3992 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
3993 <<"not generating."<<std::endl;
3997 /*dstream<<"generateBlock(): "
3998 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4001 MapBlock *block = original_dummy;
4003 v2s16 p2d(p.X, p.Z);
4005 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4008 Do not generate over-limit
4010 if(blockpos_over_limit(p))
4012 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4013 throw InvalidPositionException("generateBlock(): pos. over limit");
4017 If block doesn't exist, create one.
4018 If it exists, it is a dummy. In that case unDummify() it.
4020 NOTE: This already sets the map as the parent of the block
4024 block = sector->createBlankBlockNoInsert(block_y);
4028 // Remove the block so that nobody can get a half-generated one.
4029 sector->removeBlock(block);
4030 // Allocate the block to contain the generated data
4036 Generate a completely empty block
4038 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4039 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4041 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4045 block->setNode(v3s16(x0,y0,z0), n);
4050 Generate a proper block
4053 u8 water_material = CONTENT_WATERSOURCE;
4055 s32 lowest_ground_y = 32767;
4056 s32 highest_ground_y = -32768;
4058 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4059 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4061 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4063 //s16 surface_y = 0;
4065 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4067 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4069 // If chunks are disabled
4070 if(m_chunksize == 0)
4071 surface_y = WATER_LEVEL + 1;
4073 if(surface_y < lowest_ground_y)
4074 lowest_ground_y = surface_y;
4075 if(surface_y > highest_ground_y)
4076 highest_ground_y = surface_y;
4078 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4080 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4082 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4087 NOTE: If there are some man-made structures above the
4088 newly created block, they won't be taken into account.
4090 if(real_y > surface_y)
4091 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4097 // If node is over heightmap y, it's air or water
4098 if(real_y > surface_y)
4100 // If under water level, it's water
4101 if(real_y < WATER_LEVEL)
4103 n.d = water_material;
4104 n.setLight(LIGHTBANK_DAY,
4105 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4107 Add to transforming liquid queue (in case it'd
4110 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4111 m_transforming_liquid.push_back(real_pos);
4117 // Else it's ground or caves (air)
4120 // If it's surface_depth under ground, it's stone
4121 if(real_y <= surface_y - surface_depth)
4123 n.d = CONTENT_STONE;
4127 // It is mud if it is under the first ground
4128 // level or under water
4129 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4135 n.d = CONTENT_GRASS;
4138 //n.d = CONTENT_MUD;
4140 /*// If under water level, it's mud
4141 if(real_y < WATER_LEVEL)
4143 // Only the topmost node is grass
4144 else if(real_y <= surface_y - 1)
4147 n.d = CONTENT_GRASS;*/
4151 block->setNode(v3s16(x0,y0,z0), n);
4156 Calculate some helper variables
4159 // Completely underground if the highest part of block is under lowest
4161 // This has to be very sure; it's probably one too strict now but
4162 // that's just better.
4163 bool completely_underground =
4164 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4166 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4168 bool mostly_underwater_surface = false;
4169 if(highest_ground_y < WATER_LEVEL
4170 && some_part_underground && !completely_underground)
4171 mostly_underwater_surface = true;
4174 Get local attributes
4177 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4179 float caves_amount = 0.5;
4184 NOTE: BEWARE: Too big amount of attribute points slows verything
4186 1 interpolation from 5000 points takes 2-3ms.
4188 //TimeTaker timer("generateBlock() local attribute retrieval");
4189 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4190 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4191 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4195 //dstream<<"generateBlock(): Done"<<std::endl;
4201 // Initialize temporary table
4202 const s32 ued = MAP_BLOCKSIZE;
4203 bool underground_emptiness[ued*ued*ued];
4204 for(s32 i=0; i<ued*ued*ued; i++)
4206 underground_emptiness[i] = 0;
4213 Initialize orp and ors. Try to find if some neighboring
4214 MapBlock has a tunnel ended in its side
4218 (float)(myrand()%ued)+0.5,
4219 (float)(myrand()%ued)+0.5,
4220 (float)(myrand()%ued)+0.5
4223 bool found_existing = false;
4229 for(s16 y=0; y<ued; y++)
4230 for(s16 x=0; x<ued; x++)
4232 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4233 if(getNode(ap).d == CONTENT_AIR)
4235 orp = v3f(x+1,y+1,0);
4236 found_existing = true;
4237 goto continue_generating;
4241 catch(InvalidPositionException &e){}
4247 for(s16 y=0; y<ued; y++)
4248 for(s16 x=0; x<ued; x++)
4250 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4251 if(getNode(ap).d == CONTENT_AIR)
4253 orp = v3f(x+1,y+1,ued-1);
4254 found_existing = true;
4255 goto continue_generating;
4259 catch(InvalidPositionException &e){}
4265 for(s16 y=0; y<ued; y++)
4266 for(s16 z=0; z<ued; z++)
4268 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4269 if(getNode(ap).d == CONTENT_AIR)
4271 orp = v3f(0,y+1,z+1);
4272 found_existing = true;
4273 goto continue_generating;
4277 catch(InvalidPositionException &e){}
4283 for(s16 y=0; y<ued; y++)
4284 for(s16 z=0; z<ued; z++)
4286 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4287 if(getNode(ap).d == CONTENT_AIR)
4289 orp = v3f(ued-1,y+1,z+1);
4290 found_existing = true;
4291 goto continue_generating;
4295 catch(InvalidPositionException &e){}
4301 for(s16 x=0; x<ued; x++)
4302 for(s16 z=0; z<ued; z++)
4304 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4305 if(getNode(ap).d == CONTENT_AIR)
4307 orp = v3f(x+1,0,z+1);
4308 found_existing = true;
4309 goto continue_generating;
4313 catch(InvalidPositionException &e){}
4319 for(s16 x=0; x<ued; x++)
4320 for(s16 z=0; z<ued; z++)
4322 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4323 if(getNode(ap).d == CONTENT_AIR)
4325 orp = v3f(x+1,ued-1,z+1);
4326 found_existing = true;
4327 goto continue_generating;
4331 catch(InvalidPositionException &e){}
4333 continue_generating:
4336 Choose whether to actually generate cave
4338 bool do_generate_caves = true;
4339 // Don't generate if no part is underground
4340 if(!some_part_underground)
4342 do_generate_caves = false;
4344 // Don't generate if mostly underwater surface
4345 /*else if(mostly_underwater_surface)
4347 do_generate_caves = false;
4349 // Partly underground = cave
4350 else if(!completely_underground)
4352 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4354 // Found existing cave underground
4355 else if(found_existing && completely_underground)
4357 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4359 // Underground and no caves found
4362 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4365 if(do_generate_caves)
4368 Generate some tunnel starting from orp and ors
4370 for(u16 i=0; i<3; i++)
4373 (float)(myrand()%ued)+0.5,
4374 (float)(myrand()%ued)+0.5,
4375 (float)(myrand()%ued)+0.5
4379 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4383 for(float f=0; f<1.0; f+=0.04)
4385 v3f fp = orp + vec * f;
4386 v3s16 cp(fp.X, fp.Y, fp.Z);
4388 s16 d1 = d0 + rs - 1;
4389 for(s16 z0=d0; z0<=d1; z0++)
4391 s16 si = rs - abs(z0);
4392 for(s16 x0=-si; x0<=si-1; x0++)
4394 s16 si2 = rs - abs(x0);
4395 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4401 if(isInArea(p, ued) == false)
4403 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4415 // Set to true if has caves.
4416 // Set when some non-air is changed to air when making caves.
4417 bool has_caves = false;
4420 Apply temporary cave data to block
4423 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4424 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4426 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4428 MapNode n = block->getNode(v3s16(x0,y0,z0));
4431 if(underground_emptiness[
4432 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4433 +ued*(y0*ued/MAP_BLOCKSIZE)
4434 +(x0*ued/MAP_BLOCKSIZE)])
4436 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4445 block->setNode(v3s16(x0,y0,z0), n);
4450 This is used for guessing whether or not the block should
4451 receive sunlight from the top if the block above doesn't exist
4453 block->setIsUnderground(completely_underground);
4456 Force lighting update if some part of block is partly
4457 underground and has caves.
4459 /*if(some_part_underground && !completely_underground && has_caves)
4461 //dstream<<"Half-ground caves"<<std::endl;
4462 lighting_invalidated_blocks[block->getPos()] = block;
4465 // DEBUG: Always update lighting
4466 //lighting_invalidated_blocks[block->getPos()] = block;
4472 if(some_part_underground)
4474 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4479 for(s16 i=0; i<underground_level/4 + 1; i++)
4481 if(myrand()%50 == 0)
4484 (myrand()%(MAP_BLOCKSIZE-2))+1,
4485 (myrand()%(MAP_BLOCKSIZE-2))+1,
4486 (myrand()%(MAP_BLOCKSIZE-2))+1
4492 for(u16 i=0; i<27; i++)
4494 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4496 block->setNode(cp+g_27dirs[i], n);
4504 u16 coal_amount = 30;
4505 u16 coal_rareness = 60 / coal_amount;
4506 if(coal_rareness == 0)
4508 if(myrand()%coal_rareness == 0)
4510 u16 a = myrand() % 16;
4511 u16 amount = coal_amount * a*a*a / 1000;
4512 for(s16 i=0; i<amount; i++)
4515 (myrand()%(MAP_BLOCKSIZE-2))+1,
4516 (myrand()%(MAP_BLOCKSIZE-2))+1,
4517 (myrand()%(MAP_BLOCKSIZE-2))+1
4521 n.d = CONTENT_STONE;
4522 n.param = MINERAL_COAL;
4524 for(u16 i=0; i<27; i++)
4526 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4528 block->setNode(cp+g_27dirs[i], n);
4536 //TODO: change to iron_amount or whatever
4537 u16 iron_amount = 15;
4538 u16 iron_rareness = 60 / iron_amount;
4539 if(iron_rareness == 0)
4541 if(myrand()%iron_rareness == 0)
4543 u16 a = myrand() % 16;
4544 u16 amount = iron_amount * a*a*a / 1000;
4545 for(s16 i=0; i<amount; i++)
4548 (myrand()%(MAP_BLOCKSIZE-2))+1,
4549 (myrand()%(MAP_BLOCKSIZE-2))+1,
4550 (myrand()%(MAP_BLOCKSIZE-2))+1
4554 n.d = CONTENT_STONE;
4555 n.param = MINERAL_IRON;
4557 for(u16 i=0; i<27; i++)
4559 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4561 block->setNode(cp+g_27dirs[i], n);
4568 Create a few rats in empty blocks underground
4570 if(completely_underground)
4572 //for(u16 i=0; i<2; i++)
4575 (myrand()%(MAP_BLOCKSIZE-2))+1,
4576 (myrand()%(MAP_BLOCKSIZE-2))+1,
4577 (myrand()%(MAP_BLOCKSIZE-2))+1
4580 // Check that the place is empty
4581 //if(!is_ground_content(block->getNode(cp).d))
4584 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4585 block->addObject(obj);
4590 #endif // end of proper block generation
4593 Add block to sector.
4595 sector->insertBlock(block);
4597 // Lighting is invalid after generation.
4598 block->setLightingExpired(true);
4605 <<"lighting_invalidated_blocks.size()"
4609 <<" "<<lighting_invalidated_blocks.size()
4611 <<", "<<completely_underground
4612 <<", "<<some_part_underground
4619 MapBlock * ServerMap::createBlock(v3s16 p)
4621 DSTACKF("%s: p=(%d,%d,%d)",
4622 __FUNCTION_NAME, p.X, p.Y, p.Z);
4625 Do not create over-limit
4627 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4628 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4629 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4630 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4631 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4632 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4633 throw InvalidPositionException("createBlock(): pos. over limit");
4635 v2s16 p2d(p.X, p.Z);
4638 This will create or load a sector if not found in memory.
4639 If block exists on disk, it will be loaded.
4641 NOTE: On old save formats, this will be slow, as it generates
4642 lighting on blocks for them.
4644 ServerMapSector *sector;
4646 sector = (ServerMapSector*)createSector(p2d);
4647 assert(sector->getId() == MAPSECTOR_SERVER);
4649 catch(InvalidPositionException &e)
4651 dstream<<"createBlock: createSector() failed"<<std::endl;
4655 NOTE: This should not be done, or at least the exception
4656 should not be passed on as std::exception, because it
4657 won't be catched at all.
4659 /*catch(std::exception &e)
4661 dstream<<"createBlock: createSector() failed: "
4662 <<e.what()<<std::endl;
4667 Try to get a block from the sector
4670 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4674 block = sector->createBlankBlock(block_y);
4678 MapBlock * ServerMap::emergeBlock(
4680 bool only_from_disk,
4681 core::map<v3s16, MapBlock*> &changed_blocks,
4682 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4685 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4687 p.X, p.Y, p.Z, only_from_disk);
4690 Do not generate over-limit
4692 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4693 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4694 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4695 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4696 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4697 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4698 throw InvalidPositionException("emergeBlock(): pos. over limit");
4700 v2s16 p2d(p.X, p.Z);
4703 This will create or load a sector if not found in memory.
4704 If block exists on disk, it will be loaded.
4706 ServerMapSector *sector;
4708 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4709 assert(sector->getId() == MAPSECTOR_SERVER);
4711 catch(InvalidPositionException &e)
4713 dstream<<"emergeBlock: emergeSector() failed: "
4714 <<e.what()<<std::endl;
4715 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4717 <<"You could try to delete it."<<std::endl;
4720 catch(VersionMismatchException &e)
4722 dstream<<"emergeBlock: emergeSector() failed: "
4723 <<e.what()<<std::endl;
4724 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4726 <<"You could try to delete it."<<std::endl;
4730 NOTE: This should not be done, or at least the exception
4731 should not be passed on as std::exception, because it
4732 won't be catched at all.
4734 /*catch(std::exception &e)
4736 dstream<<"emergeBlock: emergeSector() failed: "
4737 <<e.what()<<std::endl;
4738 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4740 <<"You could try to delete it."<<std::endl;
4745 Try to get a block from the sector
4748 bool does_not_exist = false;
4749 bool lighting_expired = false;
4750 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4754 does_not_exist = true;
4756 else if(block->isDummy() == true)
4758 does_not_exist = true;
4760 else if(block->getLightingExpired())
4762 lighting_expired = true;
4767 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4772 If block was not found on disk and not going to generate a
4773 new one, make sure there is a dummy block in place.
4775 if(only_from_disk && (does_not_exist || lighting_expired))
4777 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4781 // Create dummy block
4782 block = new MapBlock(this, p, true);
4784 // Add block to sector
4785 sector->insertBlock(block);
4791 //dstream<<"Not found on disk, generating."<<std::endl;
4793 //TimeTaker("emergeBlock() generate");
4795 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4798 If the block doesn't exist, generate the block.
4802 block = generateBlock(p, block, sector, changed_blocks,
4803 lighting_invalidated_blocks);
4806 if(lighting_expired)
4808 lighting_invalidated_blocks.insert(p, block);
4812 Initially update sunlight
4816 core::map<v3s16, bool> light_sources;
4817 bool black_air_left = false;
4818 bool bottom_invalid =
4819 block->propagateSunlight(light_sources, true,
4820 &black_air_left, true);
4822 // If sunlight didn't reach everywhere and part of block is
4823 // above ground, lighting has to be properly updated
4824 //if(black_air_left && some_part_underground)
4827 lighting_invalidated_blocks[block->getPos()] = block;
4832 lighting_invalidated_blocks[block->getPos()] = block;
4839 s16 ServerMap::findGroundLevel(v2s16 p2d)
4842 Uh, just do something random...
4844 // Find existing map from top to down
4847 v3s16 p(p2d.X, max, p2d.Y);
4848 for(; p.Y>min; p.Y--)
4850 MapNode n = getNodeNoEx(p);
4851 if(n.d != CONTENT_IGNORE)
4856 // If this node is not air, go to plan b
4857 if(getNodeNoEx(p).d != CONTENT_AIR)
4859 // Search existing walkable and return it
4860 for(; p.Y>min; p.Y--)
4862 MapNode n = getNodeNoEx(p);
4863 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4869 Plan B: Get from map generator perlin noise function
4871 // This won't work if proper generation is disabled
4872 if(m_chunksize == 0)
4873 return WATER_LEVEL+2;
4874 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4878 void ServerMap::createDir(std::string path)
4880 if(fs::CreateDir(path) == false)
4882 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4883 <<"\""<<path<<"\""<<std::endl;
4884 throw BaseException("ServerMap failed to create directory");
4888 std::string ServerMap::getSectorSubDir(v2s16 pos)
4891 snprintf(cc, 9, "%.4x%.4x",
4892 (unsigned int)pos.X&0xffff,
4893 (unsigned int)pos.Y&0xffff);
4895 return std::string(cc);
4898 std::string ServerMap::getSectorDir(v2s16 pos)
4900 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4903 v2s16 ServerMap::getSectorPos(std::string dirname)
4905 if(dirname.size() != 8)
4906 throw InvalidFilenameException("Invalid sector directory name");
4908 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4910 throw InvalidFilenameException("Invalid sector directory name");
4911 v2s16 pos((s16)x, (s16)y);
4915 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4917 v2s16 p2d = getSectorPos(sectordir);
4919 if(blockfile.size() != 4){
4920 throw InvalidFilenameException("Invalid block filename");
4923 int r = sscanf(blockfile.c_str(), "%4x", &y);
4925 throw InvalidFilenameException("Invalid block filename");
4926 return v3s16(p2d.X, y, p2d.Y);
4929 void ServerMap::save(bool only_changed)
4931 DSTACK(__FUNCTION_NAME);
4932 if(m_map_saving_enabled == false)
4934 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4938 if(only_changed == false)
4939 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4942 if(only_changed == false || m_map_metadata_changed)
4947 // Disable saving chunk metadata if chunks are disabled
4948 if(m_chunksize != 0)
4950 if(only_changed == false || anyChunkModified())
4954 u32 sector_meta_count = 0;
4955 u32 block_count = 0;
4958 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4960 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4961 for(; i.atEnd() == false; i++)
4963 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4964 assert(sector->getId() == MAPSECTOR_SERVER);
4966 if(sector->differs_from_disk || only_changed == false)
4968 saveSectorMeta(sector);
4969 sector_meta_count++;
4971 core::list<MapBlock*> blocks;
4972 sector->getBlocks(blocks);
4973 core::list<MapBlock*>::Iterator j;
4974 for(j=blocks.begin(); j!=blocks.end(); j++)
4976 MapBlock *block = *j;
4977 if(block->getChangedFlag() || only_changed == false)
4982 /*dstream<<"ServerMap: Written block ("
4983 <<block->getPos().X<<","
4984 <<block->getPos().Y<<","
4985 <<block->getPos().Z<<")"
4994 Only print if something happened or saved whole map
4996 if(only_changed == false || sector_meta_count != 0
4997 || block_count != 0)
4999 dstream<<DTIME<<"ServerMap: Written: "
5000 <<sector_meta_count<<" sector metadata files, "
5001 <<block_count<<" block files"
5007 // NOTE: Doing this is insane. Deprecated and probably broken.
5008 void ServerMap::loadAll()
5010 DSTACK(__FUNCTION_NAME);
5011 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5016 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5018 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5020 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5023 s32 printed_counter = -100000;
5024 s32 count = list.size();
5026 std::vector<fs::DirListNode>::iterator i;
5027 for(i=list.begin(); i!=list.end(); i++)
5029 if(counter > printed_counter + 10)
5031 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5032 printed_counter = counter;
5036 MapSector *sector = NULL;
5038 // We want directories
5042 sector = loadSectorMeta(i->name);
5044 catch(InvalidFilenameException &e)
5046 // This catches unknown crap in directory
5049 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5050 (m_savedir+"/sectors/"+i->name);
5051 std::vector<fs::DirListNode>::iterator i2;
5052 for(i2=list2.begin(); i2!=list2.end(); i2++)
5058 loadBlock(i->name, i2->name, sector);
5060 catch(InvalidFilenameException &e)
5062 // This catches unknown crap in directory
5066 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5071 void ServerMap::saveMasterHeightmap()
5073 DSTACK(__FUNCTION_NAME);
5075 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5077 createDir(m_savedir);
5079 /*std::string fullpath = m_savedir + "/master_heightmap";
5080 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5081 if(o.good() == false)
5082 throw FileNotGoodException("Cannot open master heightmap");*/
5084 // Format used for writing
5085 //u8 version = SER_FMT_VER_HIGHEST;
5088 void ServerMap::loadMasterHeightmap()
5090 DSTACK(__FUNCTION_NAME);
5092 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5094 /*std::string fullpath = m_savedir + "/master_heightmap";
5095 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5096 if(is.good() == false)
5097 throw FileNotGoodException("Cannot open master heightmap");*/
5101 void ServerMap::saveMapMeta()
5103 DSTACK(__FUNCTION_NAME);
5105 dstream<<"INFO: ServerMap::saveMapMeta(): "
5106 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5109 createDir(m_savedir);
5111 std::string fullpath = m_savedir + "/map_meta.txt";
5112 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5113 if(os.good() == false)
5115 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5116 <<"could not open"<<fullpath<<std::endl;
5117 throw FileNotGoodException("Cannot open chunk metadata");
5121 params.setU64("seed", m_seed);
5122 params.setS32("chunksize", m_chunksize);
5124 params.writeLines(os);
5126 os<<"[end_of_params]\n";
5128 m_map_metadata_changed = false;
5131 void ServerMap::loadMapMeta()
5133 DSTACK(__FUNCTION_NAME);
5135 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5138 std::string fullpath = m_savedir + "/map_meta.txt";
5139 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5140 if(is.good() == false)
5142 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5143 <<"could not open"<<fullpath<<std::endl;
5144 throw FileNotGoodException("Cannot open map metadata");
5152 throw SerializationError
5153 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5155 std::getline(is, line);
5156 std::string trimmedline = trim(line);
5157 if(trimmedline == "[end_of_params]")
5159 params.parseConfigLine(line);
5162 m_seed = params.getU64("seed");
5163 m_chunksize = params.getS32("chunksize");
5165 dstream<<"INFO: ServerMap::loadMapMeta(): "
5166 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5170 void ServerMap::saveChunkMeta()
5172 DSTACK(__FUNCTION_NAME);
5174 // This should not be called if chunks are disabled.
5175 assert(m_chunksize != 0);
5177 u32 count = m_chunks.size();
5179 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5180 <<count<<" chunks"<<std::endl;
5182 createDir(m_savedir);
5184 std::string fullpath = m_savedir + "/chunk_meta";
5185 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5186 if(os.good() == false)
5188 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5189 <<"could not open"<<fullpath<<std::endl;
5190 throw FileNotGoodException("Cannot open chunk metadata");
5196 os.write((char*)&version, 1);
5201 writeU32(buf, count);
5202 os.write((char*)buf, 4);
5204 for(core::map<v2s16, MapChunk*>::Iterator
5205 i = m_chunks.getIterator();
5206 i.atEnd()==false; i++)
5208 v2s16 p = i.getNode()->getKey();
5209 MapChunk *chunk = i.getNode()->getValue();
5212 os.write((char*)buf, 4);
5214 chunk->serialize(os, version);
5217 setChunksNonModified();
5220 void ServerMap::loadChunkMeta()
5222 DSTACK(__FUNCTION_NAME);
5224 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5227 std::string fullpath = m_savedir + "/chunk_meta";
5228 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5229 if(is.good() == false)
5231 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5232 <<"could not open"<<fullpath<<std::endl;
5233 throw FileNotGoodException("Cannot open chunk metadata");
5239 is.read((char*)&version, 1);
5244 is.read((char*)buf, 4);
5245 u32 count = readU32(buf);
5247 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5248 <<count<<" chunks"<<std::endl;
5250 for(u32 i=0; i<count; i++)
5253 MapChunk *chunk = new MapChunk();
5255 is.read((char*)buf, 4);
5258 chunk->deSerialize(is, version);
5259 m_chunks.insert(p, chunk);
5263 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5265 DSTACK(__FUNCTION_NAME);
5266 // Format used for writing
5267 u8 version = SER_FMT_VER_HIGHEST;
5269 v2s16 pos = sector->getPos();
5270 createDir(m_savedir);
5271 createDir(m_savedir+"/sectors");
5272 std::string dir = getSectorDir(pos);
5275 std::string fullpath = dir + "/meta";
5276 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5277 if(o.good() == false)
5278 throw FileNotGoodException("Cannot open sector metafile");
5280 sector->serialize(o, version);
5282 sector->differs_from_disk = false;
5285 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5287 DSTACK(__FUNCTION_NAME);
5289 v2s16 p2d = getSectorPos(dirname);
5290 std::string dir = m_savedir + "/sectors/" + dirname;
5292 ServerMapSector *sector = NULL;
5294 std::string fullpath = dir + "/meta";
5295 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5296 if(is.good() == false)
5298 // If the directory exists anyway, it probably is in some old
5299 // format. Just go ahead and create the sector.
5300 if(fs::PathExists(dir))
5302 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5303 <<fullpath<<" doesn't exist but directory does."
5304 <<" Continuing with a sector with no metadata."
5306 sector = new ServerMapSector(this, p2d);
5307 m_sectors.insert(p2d, sector);
5310 throw FileNotGoodException("Cannot open sector metafile");
5314 sector = ServerMapSector::deSerialize
5315 (is, this, p2d, m_sectors);
5318 sector->differs_from_disk = false;
5323 bool ServerMap::loadSectorFull(v2s16 p2d)
5325 DSTACK(__FUNCTION_NAME);
5326 std::string sectorsubdir = getSectorSubDir(p2d);
5328 MapSector *sector = NULL;
5330 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5333 sector = loadSectorMeta(sectorsubdir);
5335 catch(InvalidFilenameException &e)
5339 catch(FileNotGoodException &e)
5343 catch(std::exception &e)
5351 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5352 (m_savedir+"/sectors/"+sectorsubdir);
5353 std::vector<fs::DirListNode>::iterator i2;
5354 for(i2=list2.begin(); i2!=list2.end(); i2++)
5360 loadBlock(sectorsubdir, i2->name, sector);
5362 catch(InvalidFilenameException &e)
5364 // This catches unknown crap in directory
5370 void ServerMap::saveBlock(MapBlock *block)
5372 DSTACK(__FUNCTION_NAME);
5374 Dummy blocks are not written
5376 if(block->isDummy())
5378 /*v3s16 p = block->getPos();
5379 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5380 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5384 // Format used for writing
5385 u8 version = SER_FMT_VER_HIGHEST;
5387 v3s16 p3d = block->getPos();
5388 v2s16 p2d(p3d.X, p3d.Z);
5389 createDir(m_savedir);
5390 createDir(m_savedir+"/sectors");
5391 std::string dir = getSectorDir(p2d);
5394 // Block file is map/sectors/xxxxxxxx/xxxx
5396 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5397 std::string fullpath = dir + "/" + cc;
5398 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5399 if(o.good() == false)
5400 throw FileNotGoodException("Cannot open block data");
5403 [0] u8 serialization version
5406 o.write((char*)&version, 1);
5408 block->serialize(o, version);
5411 Versions up from 9 have block objects.
5415 block->serializeObjects(o, version);
5419 Versions up from 15 have static objects.
5423 block->m_static_objects.serialize(o);
5426 // We just wrote it to the disk
5427 block->resetChangedFlag();
5430 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5432 DSTACK(__FUNCTION_NAME);
5434 // Block file is map/sectors/xxxxxxxx/xxxx
5435 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5438 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5439 if(is.good() == false)
5440 throw FileNotGoodException("Cannot open block file");
5442 v3s16 p3d = getBlockPos(sectordir, blockfile);
5443 v2s16 p2d(p3d.X, p3d.Z);
5445 assert(sector->getPos() == p2d);
5447 u8 version = SER_FMT_VER_INVALID;
5448 is.read((char*)&version, 1);
5451 throw SerializationError("ServerMap::loadBlock(): Failed"
5452 " to read MapBlock version");
5454 /*u32 block_size = MapBlock::serializedLength(version);
5455 SharedBuffer<u8> data(block_size);
5456 is.read((char*)*data, block_size);*/
5458 // This will always return a sector because we're the server
5459 //MapSector *sector = emergeSector(p2d);
5461 MapBlock *block = NULL;
5462 bool created_new = false;
5464 block = sector->getBlockNoCreate(p3d.Y);
5466 catch(InvalidPositionException &e)
5468 block = sector->createBlankBlockNoInsert(p3d.Y);
5472 // deserialize block data
5473 block->deSerialize(is, version);
5476 Versions up from 9 have block objects.
5480 block->updateObjects(is, version, NULL, 0);
5484 Versions up from 15 have static objects.
5488 block->m_static_objects.deSerialize(is);
5492 sector->insertBlock(block);
5495 Convert old formats to new and save
5498 // Save old format blocks in new format
5499 if(version < SER_FMT_VER_HIGHEST)
5504 // We just loaded it from the disk, so it's up-to-date.
5505 block->resetChangedFlag();
5508 catch(SerializationError &e)
5510 dstream<<"WARNING: Invalid block data on disk "
5511 "(SerializationError). Ignoring. "
5512 "A new one will be generated."
5515 // TODO: Backup file; name is in fullpath.
5519 void ServerMap::PrintInfo(std::ostream &out)
5530 ClientMap::ClientMap(
5532 MapDrawControl &control,
5533 scene::ISceneNode* parent,
5534 scene::ISceneManager* mgr,
5538 scene::ISceneNode(parent, mgr, id),
5541 m_camera_position(0,0,0),
5542 m_camera_direction(0,0,1)
5544 m_camera_mutex.Init();
5545 assert(m_camera_mutex.IsInitialized());
5547 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5548 BS*1000000,BS*1000000,BS*1000000);
5551 ClientMap::~ClientMap()
5553 /*JMutexAutoLock lock(mesh_mutex);
5562 MapSector * ClientMap::emergeSector(v2s16 p2d)
5564 DSTACK(__FUNCTION_NAME);
5565 // Check that it doesn't exist already
5567 return getSectorNoGenerate(p2d);
5569 catch(InvalidPositionException &e)
5574 ClientMapSector *sector = new ClientMapSector(this, p2d);
5577 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5578 m_sectors.insert(p2d, sector);
5584 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5586 DSTACK(__FUNCTION_NAME);
5587 ClientMapSector *sector = NULL;
5589 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5591 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5595 sector = (ClientMapSector*)n->getValue();
5596 assert(sector->getId() == MAPSECTOR_CLIENT);
5600 sector = new ClientMapSector(this, p2d);
5602 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5603 m_sectors.insert(p2d, sector);
5607 sector->deSerialize(is);
5610 void ClientMap::OnRegisterSceneNode()
5614 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5615 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5618 ISceneNode::OnRegisterSceneNode();
5621 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5623 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5624 DSTACK(__FUNCTION_NAME);
5626 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5629 Get time for measuring timeout.
5631 Measuring time is very useful for long delays when the
5632 machine is swapping a lot.
5634 int time1 = time(0);
5636 //u32 daynight_ratio = m_client->getDayNightRatio();
5638 m_camera_mutex.Lock();
5639 v3f camera_position = m_camera_position;
5640 v3f camera_direction = m_camera_direction;
5641 m_camera_mutex.Unlock();
5644 Get all blocks and draw all visible ones
5647 v3s16 cam_pos_nodes(
5648 camera_position.X / BS,
5649 camera_position.Y / BS,
5650 camera_position.Z / BS);
5652 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5654 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5655 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5657 // Take a fair amount as we will be dropping more out later
5659 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5660 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5661 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5663 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5664 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5665 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5667 u32 vertex_count = 0;
5669 // For limiting number of mesh updates per frame
5670 u32 mesh_update_count = 0;
5672 u32 blocks_would_have_drawn = 0;
5673 u32 blocks_drawn = 0;
5675 //NOTE: The sectors map should be locked but we're not doing it
5676 // because it'd cause too much delays
5678 int timecheck_counter = 0;
5679 core::map<v2s16, MapSector*>::Iterator si;
5680 si = m_sectors.getIterator();
5681 for(; si.atEnd() == false; si++)
5684 timecheck_counter++;
5685 if(timecheck_counter > 50)
5687 timecheck_counter = 0;
5688 int time2 = time(0);
5689 if(time2 > time1 + 4)
5691 dstream<<"ClientMap::renderMap(): "
5692 "Rendering takes ages, returning."
5699 MapSector *sector = si.getNode()->getValue();
5700 v2s16 sp = sector->getPos();
5702 if(m_control.range_all == false)
5704 if(sp.X < p_blocks_min.X
5705 || sp.X > p_blocks_max.X
5706 || sp.Y < p_blocks_min.Z
5707 || sp.Y > p_blocks_max.Z)
5711 core::list< MapBlock * > sectorblocks;
5712 sector->getBlocks(sectorblocks);
5718 core::list< MapBlock * >::Iterator i;
5719 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5721 MapBlock *block = *i;
5724 Compare block position to camera position, skip
5725 if not seen on display
5728 float range = 100000 * BS;
5729 if(m_control.range_all == false)
5730 range = m_control.wanted_range * BS;
5733 if(isBlockInSight(block->getPos(), camera_position,
5734 camera_direction, range, &d) == false)
5739 // This is ugly (spherical distance limit?)
5740 /*if(m_control.range_all == false &&
5741 d - 0.5*BS*MAP_BLOCKSIZE > range)
5746 Update expired mesh (used for day/night change)
5748 It doesn't work exactly like it should now with the
5749 tasked mesh update but whatever.
5752 bool mesh_expired = false;
5755 JMutexAutoLock lock(block->mesh_mutex);
5757 mesh_expired = block->getMeshExpired();
5759 // Mesh has not been expired and there is no mesh:
5760 // block has no content
5761 if(block->mesh == NULL && mesh_expired == false)
5765 f32 faraway = BS*50;
5766 //f32 faraway = m_control.wanted_range * BS;
5769 This has to be done with the mesh_mutex unlocked
5771 // Pretty random but this should work somewhat nicely
5772 if(mesh_expired && (
5773 (mesh_update_count < 3
5774 && (d < faraway || mesh_update_count < 2)
5777 (m_control.range_all && mesh_update_count < 20)
5780 /*if(mesh_expired && mesh_update_count < 6
5781 && (d < faraway || mesh_update_count < 3))*/
5783 mesh_update_count++;
5785 // Mesh has been expired: generate new mesh
5786 //block->updateMesh(daynight_ratio);
5787 m_client->addUpdateMeshTask(block->getPos());
5789 mesh_expired = false;
5794 Draw the faces of the block
5797 JMutexAutoLock lock(block->mesh_mutex);
5799 scene::SMesh *mesh = block->mesh;
5804 blocks_would_have_drawn++;
5805 if(blocks_drawn >= m_control.wanted_max_blocks
5806 && m_control.range_all == false
5807 && d > m_control.wanted_min_range * BS)
5811 u32 c = mesh->getMeshBufferCount();
5813 for(u32 i=0; i<c; i++)
5815 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5816 const video::SMaterial& material = buf->getMaterial();
5817 video::IMaterialRenderer* rnd =
5818 driver->getMaterialRenderer(material.MaterialType);
5819 bool transparent = (rnd && rnd->isTransparent());
5820 // Render transparent on transparent pass and likewise.
5821 if(transparent == is_transparent_pass)
5824 This *shouldn't* hurt too much because Irrlicht
5825 doesn't change opengl textures if the old
5826 material is set again.
5828 driver->setMaterial(buf->getMaterial());
5829 driver->drawMeshBuffer(buf);
5830 vertex_count += buf->getVertexCount();
5834 } // foreach sectorblocks
5837 m_control.blocks_drawn = blocks_drawn;
5838 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5840 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5841 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5844 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5845 core::map<v3s16, MapBlock*> *affected_blocks)
5847 bool changed = false;
5849 Add it to all blocks touching it
5852 v3s16(0,0,0), // this
5853 v3s16(0,0,1), // back
5854 v3s16(0,1,0), // top
5855 v3s16(1,0,0), // right
5856 v3s16(0,0,-1), // front
5857 v3s16(0,-1,0), // bottom
5858 v3s16(-1,0,0), // left
5860 for(u16 i=0; i<7; i++)
5862 v3s16 p2 = p + dirs[i];
5863 // Block position of neighbor (or requested) node
5864 v3s16 blockpos = getNodeBlockPos(p2);
5865 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5866 if(blockref == NULL)
5868 // Relative position of requested node
5869 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5870 if(blockref->setTempMod(relpos, mod))
5875 if(changed && affected_blocks!=NULL)
5877 for(u16 i=0; i<7; i++)
5879 v3s16 p2 = p + dirs[i];
5880 // Block position of neighbor (or requested) node
5881 v3s16 blockpos = getNodeBlockPos(p2);
5882 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5883 if(blockref == NULL)
5885 affected_blocks->insert(blockpos, blockref);
5891 bool ClientMap::clearTempMod(v3s16 p,
5892 core::map<v3s16, MapBlock*> *affected_blocks)
5894 bool changed = false;
5896 v3s16(0,0,0), // this
5897 v3s16(0,0,1), // back
5898 v3s16(0,1,0), // top
5899 v3s16(1,0,0), // right
5900 v3s16(0,0,-1), // front
5901 v3s16(0,-1,0), // bottom
5902 v3s16(-1,0,0), // left
5904 for(u16 i=0; i<7; i++)
5906 v3s16 p2 = p + dirs[i];
5907 // Block position of neighbor (or requested) node
5908 v3s16 blockpos = getNodeBlockPos(p2);
5909 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5910 if(blockref == NULL)
5912 // Relative position of requested node
5913 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5914 if(blockref->clearTempMod(relpos))
5919 if(changed && affected_blocks!=NULL)
5921 for(u16 i=0; i<7; i++)
5923 v3s16 p2 = p + dirs[i];
5924 // Block position of neighbor (or requested) node
5925 v3s16 blockpos = getNodeBlockPos(p2);
5926 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5927 if(blockref == NULL)
5929 affected_blocks->insert(blockpos, blockref);
5935 void ClientMap::expireMeshes(bool only_daynight_diffed)
5937 TimeTaker timer("expireMeshes()");
5939 core::map<v2s16, MapSector*>::Iterator si;
5940 si = m_sectors.getIterator();
5941 for(; si.atEnd() == false; si++)
5943 MapSector *sector = si.getNode()->getValue();
5945 core::list< MapBlock * > sectorblocks;
5946 sector->getBlocks(sectorblocks);
5948 core::list< MapBlock * >::Iterator i;
5949 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5951 MapBlock *block = *i;
5953 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5959 JMutexAutoLock lock(block->mesh_mutex);
5960 if(block->mesh != NULL)
5962 /*block->mesh->drop();
5963 block->mesh = NULL;*/
5964 block->setMeshExpired(true);
5971 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5973 assert(mapType() == MAPTYPE_CLIENT);
5976 v3s16 p = blockpos + v3s16(0,0,0);
5977 MapBlock *b = getBlockNoCreate(p);
5978 b->updateMesh(daynight_ratio);
5979 //b->setMeshExpired(true);
5981 catch(InvalidPositionException &e){}
5984 v3s16 p = blockpos + v3s16(-1,0,0);
5985 MapBlock *b = getBlockNoCreate(p);
5986 b->updateMesh(daynight_ratio);
5987 //b->setMeshExpired(true);
5989 catch(InvalidPositionException &e){}
5991 v3s16 p = blockpos + v3s16(0,-1,0);
5992 MapBlock *b = getBlockNoCreate(p);
5993 b->updateMesh(daynight_ratio);
5994 //b->setMeshExpired(true);
5996 catch(InvalidPositionException &e){}
5998 v3s16 p = blockpos + v3s16(0,0,-1);
5999 MapBlock *b = getBlockNoCreate(p);
6000 b->updateMesh(daynight_ratio);
6001 //b->setMeshExpired(true);
6003 catch(InvalidPositionException &e){}
6008 Update mesh of block in which the node is, and if the node is at the
6009 leading edge, update the appropriate leading blocks too.
6011 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6019 v3s16 blockposes[4];
6020 for(u32 i=0; i<4; i++)
6022 v3s16 np = nodepos + dirs[i];
6023 blockposes[i] = getNodeBlockPos(np);
6024 // Don't update mesh of block if it has been done already
6025 bool already_updated = false;
6026 for(u32 j=0; j<i; j++)
6028 if(blockposes[j] == blockposes[i])
6030 already_updated = true;
6037 MapBlock *b = getBlockNoCreate(blockposes[i]);
6038 b->updateMesh(daynight_ratio);
6043 void ClientMap::PrintInfo(std::ostream &out)
6054 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6059 MapVoxelManipulator::~MapVoxelManipulator()
6061 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6065 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6067 TimeTaker timer1("emerge", &emerge_time);
6069 // Units of these are MapBlocks
6070 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6071 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6073 VoxelArea block_area_nodes
6074 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6076 addArea(block_area_nodes);
6078 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6079 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6080 for(s32 x=p_min.X; x<=p_max.X; x++)
6083 core::map<v3s16, bool>::Node *n;
6084 n = m_loaded_blocks.find(p);
6088 bool block_data_inexistent = false;
6091 TimeTaker timer1("emerge load", &emerge_load_time);
6093 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6094 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6097 dstream<<std::endl;*/
6099 MapBlock *block = m_map->getBlockNoCreate(p);
6100 if(block->isDummy())
6101 block_data_inexistent = true;
6103 block->copyTo(*this);
6105 catch(InvalidPositionException &e)
6107 block_data_inexistent = true;
6110 if(block_data_inexistent)
6112 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6113 // Fill with VOXELFLAG_INEXISTENT
6114 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6115 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6117 s32 i = m_area.index(a.MinEdge.X,y,z);
6118 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6122 m_loaded_blocks.insert(p, !block_data_inexistent);
6125 //dstream<<"emerge done"<<std::endl;
6129 SUGG: Add an option to only update eg. water and air nodes.
6130 This will make it interfere less with important stuff if
6133 void MapVoxelManipulator::blitBack
6134 (core::map<v3s16, MapBlock*> & modified_blocks)
6136 if(m_area.getExtent() == v3s16(0,0,0))
6139 //TimeTaker timer1("blitBack");
6141 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6142 <<m_loaded_blocks.size()<<std::endl;*/
6145 Initialize block cache
6147 v3s16 blockpos_last;
6148 MapBlock *block = NULL;
6149 bool block_checked_in_modified = false;
6151 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6152 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6153 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6157 u8 f = m_flags[m_area.index(p)];
6158 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6161 MapNode &n = m_data[m_area.index(p)];
6163 v3s16 blockpos = getNodeBlockPos(p);
6168 if(block == NULL || blockpos != blockpos_last){
6169 block = m_map->getBlockNoCreate(blockpos);
6170 blockpos_last = blockpos;
6171 block_checked_in_modified = false;
6174 // Calculate relative position in block
6175 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6177 // Don't continue if nothing has changed here
6178 if(block->getNode(relpos) == n)
6181 //m_map->setNode(m_area.MinEdge + p, n);
6182 block->setNode(relpos, n);
6185 Make sure block is in modified_blocks
6187 if(block_checked_in_modified == false)
6189 modified_blocks[blockpos] = block;
6190 block_checked_in_modified = true;
6193 catch(InvalidPositionException &e)
6199 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6200 MapVoxelManipulator(map),
6201 m_create_area(false)
6205 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6209 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6211 // Just create the area so that it can be pointed to
6212 VoxelManipulator::emerge(a, caller_id);
6215 void ManualMapVoxelManipulator::initialEmerge(
6216 v3s16 blockpos_min, v3s16 blockpos_max)
6218 TimeTaker timer1("initialEmerge", &emerge_time);
6220 // Units of these are MapBlocks
6221 v3s16 p_min = blockpos_min;
6222 v3s16 p_max = blockpos_max;
6224 VoxelArea block_area_nodes
6225 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6227 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6230 dstream<<"initialEmerge: area: ";
6231 block_area_nodes.print(dstream);
6232 dstream<<" ("<<size_MB<<"MB)";
6236 addArea(block_area_nodes);
6238 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6239 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6240 for(s32 x=p_min.X; x<=p_max.X; x++)
6243 core::map<v3s16, bool>::Node *n;
6244 n = m_loaded_blocks.find(p);
6248 bool block_data_inexistent = false;
6251 TimeTaker timer1("emerge load", &emerge_load_time);
6253 MapBlock *block = m_map->getBlockNoCreate(p);
6254 if(block->isDummy())
6255 block_data_inexistent = true;
6257 block->copyTo(*this);
6259 catch(InvalidPositionException &e)
6261 block_data_inexistent = true;
6264 if(block_data_inexistent)
6267 Mark area inexistent
6269 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6270 // Fill with VOXELFLAG_INEXISTENT
6271 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6272 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6274 s32 i = m_area.index(a.MinEdge.X,y,z);
6275 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6279 m_loaded_blocks.insert(p, !block_data_inexistent);
6283 void ManualMapVoxelManipulator::blitBackAll(
6284 core::map<v3s16, MapBlock*> * modified_blocks)
6286 if(m_area.getExtent() == v3s16(0,0,0))
6290 Copy data of all blocks
6292 for(core::map<v3s16, bool>::Iterator
6293 i = m_loaded_blocks.getIterator();
6294 i.atEnd() == false; i++)
6296 bool existed = i.getNode()->getValue();
6297 if(existed == false)
6299 v3s16 p = i.getNode()->getKey();
6300 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6303 dstream<<"WARNING: "<<__FUNCTION_NAME
6304 <<": got NULL block "
6305 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6310 block->copyFrom(*this);
6313 modified_blocks->insert(p, block);