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"
31 #include "content_mapnode.h"
37 SQLite format specification:
38 - Initially only replaces sectors/ and sectors2/
45 Map::Map(std::ostream &dout):
49 /*m_sector_mutex.Init();
50 assert(m_sector_mutex.IsInitialized());*/
58 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
59 for(; i.atEnd() == false; i++)
61 MapSector *sector = i.getNode()->getValue();
66 void Map::addEventReceiver(MapEventReceiver *event_receiver)
68 m_event_receivers.insert(event_receiver, false);
71 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
73 if(m_event_receivers.find(event_receiver) == NULL)
75 m_event_receivers.remove(event_receiver);
78 void Map::dispatchEvent(MapEditEvent *event)
80 for(core::map<MapEventReceiver*, bool>::Iterator
81 i = m_event_receivers.getIterator();
82 i.atEnd()==false; i++)
84 MapEventReceiver* event_receiver = i.getNode()->getKey();
85 event_receiver->onMapEditEvent(event);
89 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
91 if(m_sector_cache != NULL && p == m_sector_cache_p){
92 MapSector * sector = m_sector_cache;
96 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
101 MapSector *sector = n->getValue();
103 // Cache the last result
104 m_sector_cache_p = p;
105 m_sector_cache = sector;
110 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
112 return getSectorNoGenerateNoExNoLock(p);
115 MapSector * Map::getSectorNoGenerate(v2s16 p)
117 MapSector *sector = getSectorNoGenerateNoEx(p);
119 throw InvalidPositionException();
124 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
129 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
134 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
138 v2s16 p2d(p3d.X, p3d.Z);
139 MapSector * sector = getSectorNoGenerate(p2d);
140 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
143 catch(InvalidPositionException &e)
149 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
151 v2s16 p2d(p3d.X, p3d.Z);
152 MapSector * sector = getSectorCreate(p2d);
154 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
157 block = sector->createBlankBlock(p3d.Y);
161 bool Map::isNodeUnderground(v3s16 p)
163 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock * block = getBlockNoCreate(blockpos);
166 return block->getIsUnderground();
168 catch(InvalidPositionException &e)
175 Goes recursively through the neighbours of the node.
177 Alters only transparent nodes.
179 If the lighting of the neighbour is lower than the lighting of
180 the node was (before changing it to 0 at the step before), the
181 lighting of the neighbour is set to 0 and then the same stuff
182 repeats for the neighbour.
184 The ending nodes of the routine are stored in light_sources.
185 This is useful when a light is removed. In such case, this
186 routine can be called for the light node and then again for
187 light_sources to re-light the area without the removed light.
189 values of from_nodes are lighting values.
191 void Map::unspreadLight(enum LightBank bank,
192 core::map<v3s16, u8> & from_nodes,
193 core::map<v3s16, bool> & light_sources,
194 core::map<v3s16, MapBlock*> & modified_blocks)
197 v3s16(0,0,1), // back
199 v3s16(1,0,0), // right
200 v3s16(0,0,-1), // front
201 v3s16(0,-1,0), // bottom
202 v3s16(-1,0,0), // left
205 if(from_nodes.size() == 0)
208 u32 blockchangecount = 0;
210 core::map<v3s16, u8> unlighted_nodes;
211 core::map<v3s16, u8>::Iterator j;
212 j = from_nodes.getIterator();
215 Initialize block cache
218 MapBlock *block = NULL;
219 // Cache this a bit, too
220 bool block_checked_in_modified = false;
222 for(; j.atEnd() == false; j++)
224 v3s16 pos = j.getNode()->getKey();
225 v3s16 blockpos = getNodeBlockPos(pos);
227 // Only fetch a new block if the block position has changed
229 if(block == NULL || blockpos != blockpos_last){
230 block = getBlockNoCreate(blockpos);
231 blockpos_last = blockpos;
233 block_checked_in_modified = false;
237 catch(InvalidPositionException &e)
245 // Calculate relative position in block
246 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
248 // Get node straight from the block
249 MapNode n = block->getNode(relpos);
251 u8 oldlight = j.getNode()->getValue();
253 // Loop through 6 neighbors
254 for(u16 i=0; i<6; i++)
256 // Get the position of the neighbor node
257 v3s16 n2pos = pos + dirs[i];
259 // Get the block where the node is located
260 v3s16 blockpos = getNodeBlockPos(n2pos);
264 // Only fetch a new block if the block position has changed
266 if(block == NULL || blockpos != blockpos_last){
267 block = getBlockNoCreate(blockpos);
268 blockpos_last = blockpos;
270 block_checked_in_modified = false;
274 catch(InvalidPositionException &e)
279 // Calculate relative position in block
280 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
281 // Get node straight from the block
282 MapNode n2 = block->getNode(relpos);
284 bool changed = false;
286 //TODO: Optimize output by optimizing light_sources?
289 If the neighbor is dimmer than what was specified
290 as oldlight (the light of the previous node)
292 if(n2.getLight(bank) < oldlight)
295 And the neighbor is transparent and it has some light
297 if(n2.light_propagates() && n2.getLight(bank) != 0)
300 Set light to 0 and add to queue
303 u8 current_light = n2.getLight(bank);
304 n2.setLight(bank, 0);
305 block->setNode(relpos, n2);
307 unlighted_nodes.insert(n2pos, current_light);
311 Remove from light_sources if it is there
312 NOTE: This doesn't happen nearly at all
314 /*if(light_sources.find(n2pos))
316 std::cout<<"Removed from light_sources"<<std::endl;
317 light_sources.remove(n2pos);
322 if(light_sources.find(n2pos) != NULL)
323 light_sources.remove(n2pos);*/
326 light_sources.insert(n2pos, true);
329 // Add to modified_blocks
330 if(changed == true && block_checked_in_modified == false)
332 // If the block is not found in modified_blocks, add.
333 if(modified_blocks.find(blockpos) == NULL)
335 modified_blocks.insert(blockpos, block);
337 block_checked_in_modified = true;
340 catch(InvalidPositionException &e)
347 /*dstream<<"unspreadLight(): Changed block "
348 <<blockchangecount<<" times"
349 <<" for "<<from_nodes.size()<<" nodes"
352 if(unlighted_nodes.size() > 0)
353 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
357 A single-node wrapper of the above
359 void Map::unLightNeighbors(enum LightBank bank,
360 v3s16 pos, u8 lightwas,
361 core::map<v3s16, bool> & light_sources,
362 core::map<v3s16, MapBlock*> & modified_blocks)
364 core::map<v3s16, u8> from_nodes;
365 from_nodes.insert(pos, lightwas);
367 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
371 Lights neighbors of from_nodes, collects all them and then
374 void Map::spreadLight(enum LightBank bank,
375 core::map<v3s16, bool> & from_nodes,
376 core::map<v3s16, MapBlock*> & modified_blocks)
378 const v3s16 dirs[6] = {
379 v3s16(0,0,1), // back
381 v3s16(1,0,0), // right
382 v3s16(0,0,-1), // front
383 v3s16(0,-1,0), // bottom
384 v3s16(-1,0,0), // left
387 if(from_nodes.size() == 0)
390 u32 blockchangecount = 0;
392 core::map<v3s16, bool> lighted_nodes;
393 core::map<v3s16, bool>::Iterator j;
394 j = from_nodes.getIterator();
397 Initialize block cache
400 MapBlock *block = NULL;
401 // Cache this a bit, too
402 bool block_checked_in_modified = false;
404 for(; j.atEnd() == false; j++)
405 //for(; j != from_nodes.end(); j++)
407 v3s16 pos = j.getNode()->getKey();
409 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
410 v3s16 blockpos = getNodeBlockPos(pos);
412 // Only fetch a new block if the block position has changed
414 if(block == NULL || blockpos != blockpos_last){
415 block = getBlockNoCreate(blockpos);
416 blockpos_last = blockpos;
418 block_checked_in_modified = false;
422 catch(InvalidPositionException &e)
430 // Calculate relative position in block
431 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
433 // Get node straight from the block
434 MapNode n = block->getNode(relpos);
436 u8 oldlight = n.getLight(bank);
437 u8 newlight = diminish_light(oldlight);
439 // Loop through 6 neighbors
440 for(u16 i=0; i<6; i++){
441 // Get the position of the neighbor node
442 v3s16 n2pos = pos + dirs[i];
444 // Get the block where the node is located
445 v3s16 blockpos = getNodeBlockPos(n2pos);
449 // Only fetch a new block if the block position has changed
451 if(block == NULL || blockpos != blockpos_last){
452 block = getBlockNoCreate(blockpos);
453 blockpos_last = blockpos;
455 block_checked_in_modified = false;
459 catch(InvalidPositionException &e)
464 // Calculate relative position in block
465 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
466 // Get node straight from the block
467 MapNode n2 = block->getNode(relpos);
469 bool changed = false;
471 If the neighbor is brighter than the current node,
472 add to list (it will light up this node on its turn)
474 if(n2.getLight(bank) > undiminish_light(oldlight))
476 lighted_nodes.insert(n2pos, true);
477 //lighted_nodes.push_back(n2pos);
481 If the neighbor is dimmer than how much light this node
482 would spread on it, add to list
484 if(n2.getLight(bank) < newlight)
486 if(n2.light_propagates())
488 n2.setLight(bank, newlight);
489 block->setNode(relpos, n2);
490 lighted_nodes.insert(n2pos, true);
491 //lighted_nodes.push_back(n2pos);
496 // Add to modified_blocks
497 if(changed == true && block_checked_in_modified == false)
499 // If the block is not found in modified_blocks, add.
500 if(modified_blocks.find(blockpos) == NULL)
502 modified_blocks.insert(blockpos, block);
504 block_checked_in_modified = true;
507 catch(InvalidPositionException &e)
514 /*dstream<<"spreadLight(): Changed block "
515 <<blockchangecount<<" times"
516 <<" for "<<from_nodes.size()<<" nodes"
519 if(lighted_nodes.size() > 0)
520 spreadLight(bank, lighted_nodes, modified_blocks);
524 A single-node source variation of the above.
526 void Map::lightNeighbors(enum LightBank bank,
528 core::map<v3s16, MapBlock*> & modified_blocks)
530 core::map<v3s16, bool> from_nodes;
531 from_nodes.insert(pos, true);
532 spreadLight(bank, from_nodes, modified_blocks);
535 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
538 v3s16(0,0,1), // back
540 v3s16(1,0,0), // right
541 v3s16(0,0,-1), // front
542 v3s16(0,-1,0), // bottom
543 v3s16(-1,0,0), // left
546 u8 brightest_light = 0;
547 v3s16 brightest_pos(0,0,0);
548 bool found_something = false;
550 // Loop through 6 neighbors
551 for(u16 i=0; i<6; i++){
552 // Get the position of the neighbor node
553 v3s16 n2pos = p + dirs[i];
558 catch(InvalidPositionException &e)
562 if(n2.getLight(bank) > brightest_light || found_something == false){
563 brightest_light = n2.getLight(bank);
564 brightest_pos = n2pos;
565 found_something = true;
569 if(found_something == false)
570 throw InvalidPositionException();
572 return brightest_pos;
576 Propagates sunlight down from a node.
577 Starting point gets sunlight.
579 Returns the lowest y value of where the sunlight went.
581 Mud is turned into grass in where the sunlight stops.
583 s16 Map::propagateSunlight(v3s16 start,
584 core::map<v3s16, MapBlock*> & modified_blocks)
589 v3s16 pos(start.X, y, start.Z);
591 v3s16 blockpos = getNodeBlockPos(pos);
594 block = getBlockNoCreate(blockpos);
596 catch(InvalidPositionException &e)
601 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
602 MapNode n = block->getNode(relpos);
604 if(n.sunlight_propagates())
606 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
607 block->setNode(relpos, n);
609 modified_blocks.insert(blockpos, block);
613 /*// Turn mud into grass
614 if(n.d == CONTENT_MUD)
617 block->setNode(relpos, n);
618 modified_blocks.insert(blockpos, block);
621 // Sunlight goes no further
628 void Map::updateLighting(enum LightBank bank,
629 core::map<v3s16, MapBlock*> & a_blocks,
630 core::map<v3s16, MapBlock*> & modified_blocks)
632 /*m_dout<<DTIME<<"Map::updateLighting(): "
633 <<a_blocks.size()<<" blocks."<<std::endl;*/
635 //TimeTaker timer("updateLighting");
639 //u32 count_was = modified_blocks.size();
641 core::map<v3s16, MapBlock*> blocks_to_update;
643 core::map<v3s16, bool> light_sources;
645 core::map<v3s16, u8> unlight_from;
647 core::map<v3s16, MapBlock*>::Iterator i;
648 i = a_blocks.getIterator();
649 for(; i.atEnd() == false; i++)
651 MapBlock *block = i.getNode()->getValue();
655 // Don't bother with dummy blocks.
659 v3s16 pos = block->getPos();
660 modified_blocks.insert(pos, block);
662 blocks_to_update.insert(pos, block);
665 Clear all light from block
667 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
668 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
669 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
674 MapNode n = block->getNode(v3s16(x,y,z));
675 u8 oldlight = n.getLight(bank);
677 block->setNode(v3s16(x,y,z), n);
679 // Collect borders for unlighting
680 if(x==0 || x == MAP_BLOCKSIZE-1
681 || y==0 || y == MAP_BLOCKSIZE-1
682 || z==0 || z == MAP_BLOCKSIZE-1)
684 v3s16 p_map = p + v3s16(
687 MAP_BLOCKSIZE*pos.Z);
688 unlight_from.insert(p_map, oldlight);
691 catch(InvalidPositionException &e)
694 This would happen when dealing with a
698 dstream<<"updateLighting(): InvalidPositionException"
703 if(bank == LIGHTBANK_DAY)
705 bool bottom_valid = block->propagateSunlight(light_sources);
707 // If bottom is valid, we're done.
711 else if(bank == LIGHTBANK_NIGHT)
713 // For night lighting, sunlight is not propagated
718 // Invalid lighting bank
722 /*dstream<<"Bottom for sunlight-propagated block ("
723 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
726 // Bottom sunlight is not valid; get the block and loop to it
730 block = getBlockNoCreate(pos);
732 catch(InvalidPositionException &e)
742 TimeTaker timer("unspreadLight");
743 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
748 u32 diff = modified_blocks.size() - count_was;
749 count_was = modified_blocks.size();
750 dstream<<"unspreadLight modified "<<diff<<std::endl;
754 TimeTaker timer("spreadLight");
755 spreadLight(bank, light_sources, modified_blocks);
760 u32 diff = modified_blocks.size() - count_was;
761 count_was = modified_blocks.size();
762 dstream<<"spreadLight modified "<<diff<<std::endl;
767 //MapVoxelManipulator vmanip(this);
769 // Make a manual voxel manipulator and load all the blocks
770 // that touch the requested blocks
771 ManualMapVoxelManipulator vmanip(this);
772 core::map<v3s16, MapBlock*>::Iterator i;
773 i = blocks_to_update.getIterator();
774 for(; i.atEnd() == false; i++)
776 MapBlock *block = i.getNode()->getValue();
777 v3s16 p = block->getPos();
779 // Add all surrounding blocks
780 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
783 Add all surrounding blocks that have up-to-date lighting
784 NOTE: This doesn't quite do the job (not everything
785 appropriate is lighted)
787 /*for(s16 z=-1; z<=1; z++)
788 for(s16 y=-1; y<=1; y++)
789 for(s16 x=-1; x<=1; x++)
792 MapBlock *block = getBlockNoCreateNoEx(p);
797 if(block->getLightingExpired())
799 vmanip.initialEmerge(p, p);
802 // Lighting of block will be updated completely
803 block->setLightingExpired(false);
807 //TimeTaker timer("unSpreadLight");
808 vmanip.unspreadLight(bank, unlight_from, light_sources);
811 //TimeTaker timer("spreadLight");
812 vmanip.spreadLight(bank, light_sources);
815 //TimeTaker timer("blitBack");
816 vmanip.blitBack(modified_blocks);
818 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
822 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
825 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
826 core::map<v3s16, MapBlock*> & modified_blocks)
828 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
829 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
832 Update information about whether day and night light differ
834 for(core::map<v3s16, MapBlock*>::Iterator
835 i = modified_blocks.getIterator();
836 i.atEnd() == false; i++)
838 MapBlock *block = i.getNode()->getValue();
839 block->updateDayNightDiff();
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)
883 If the new node is solid and there is grass below, change it to mud
885 if(content_features(n.d).walkable == true)
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)
905 If the new node is mud and it is under sunlight, change it
908 if(n.d == CONTENT_MUD && node_under_sunlight)
915 Remove all light that has come out of this node
918 enum LightBank banks[] =
923 for(s32 i=0; i<2; i++)
925 enum LightBank bank = banks[i];
927 u8 lightwas = getNode(p).getLight(bank);
929 // Add the block of the added node to modified_blocks
930 v3s16 blockpos = getNodeBlockPos(p);
931 MapBlock * block = getBlockNoCreate(blockpos);
932 assert(block != NULL);
933 modified_blocks.insert(blockpos, block);
935 assert(isValidPosition(p));
937 // Unlight neighbours of node.
938 // This means setting light of all consequent dimmer nodes
940 // This also collects the nodes at the border which will spread
941 // light again into this.
942 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
948 If node lets sunlight through and is under sunlight, it has
951 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
953 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
957 Set the node on the map
966 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
969 NodeMetadata *meta = meta_proto->clone();
970 setNodeMetadata(p, meta);
974 If node is under sunlight and doesn't let sunlight through,
975 take all sunlighted nodes under it and clear light from them
976 and from where the light has been spread.
977 TODO: This could be optimized by mass-unlighting instead
980 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
984 //m_dout<<DTIME<<"y="<<y<<std::endl;
985 v3s16 n2pos(p.X, y, p.Z);
991 catch(InvalidPositionException &e)
996 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
998 unLightNeighbors(LIGHTBANK_DAY,
999 n2pos, n2.getLight(LIGHTBANK_DAY),
1000 light_sources, modified_blocks);
1001 n2.setLight(LIGHTBANK_DAY, 0);
1009 for(s32 i=0; i<2; i++)
1011 enum LightBank bank = banks[i];
1014 Spread light from all nodes that might be capable of doing so
1016 spreadLight(bank, light_sources, modified_blocks);
1020 Update information about whether day and night light differ
1022 for(core::map<v3s16, MapBlock*>::Iterator
1023 i = modified_blocks.getIterator();
1024 i.atEnd() == false; i++)
1026 MapBlock *block = i.getNode()->getValue();
1027 block->updateDayNightDiff();
1031 Add neighboring liquid nodes and the node itself if it is
1032 liquid (=water node was added) to transform queue.
1035 v3s16(0,0,0), // self
1036 v3s16(0,0,1), // back
1037 v3s16(0,1,0), // top
1038 v3s16(1,0,0), // right
1039 v3s16(0,0,-1), // front
1040 v3s16(0,-1,0), // bottom
1041 v3s16(-1,0,0), // left
1043 for(u16 i=0; i<7; i++)
1048 v3s16 p2 = p + dirs[i];
1050 MapNode n2 = getNode(p2);
1051 if(content_liquid(n2.d))
1053 m_transforming_liquid.push_back(p2);
1056 }catch(InvalidPositionException &e)
1064 void Map::removeNodeAndUpdate(v3s16 p,
1065 core::map<v3s16, MapBlock*> &modified_blocks)
1067 /*PrintInfo(m_dout);
1068 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1069 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1071 bool node_under_sunlight = true;
1073 v3s16 toppos = p + v3s16(0,1,0);
1075 // Node will be replaced with this
1076 u8 replace_material = CONTENT_AIR;
1079 If there is a node at top and it doesn't have sunlight,
1080 there will be no sunlight going down.
1083 MapNode topnode = getNode(toppos);
1085 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1086 node_under_sunlight = false;
1088 catch(InvalidPositionException &e)
1092 core::map<v3s16, bool> light_sources;
1094 enum LightBank banks[] =
1099 for(s32 i=0; i<2; i++)
1101 enum LightBank bank = banks[i];
1104 Unlight neighbors (in case the node is a light source)
1106 unLightNeighbors(bank, p,
1107 getNode(p).getLight(bank),
1108 light_sources, modified_blocks);
1112 Remove node metadata
1115 removeNodeMetadata(p);
1119 This also clears the lighting.
1123 n.d = replace_material;
1126 for(s32 i=0; i<2; i++)
1128 enum LightBank bank = banks[i];
1131 Recalculate lighting
1133 spreadLight(bank, light_sources, modified_blocks);
1136 // Add the block of the removed node to modified_blocks
1137 v3s16 blockpos = getNodeBlockPos(p);
1138 MapBlock * block = getBlockNoCreate(blockpos);
1139 assert(block != NULL);
1140 modified_blocks.insert(blockpos, block);
1143 If the removed node was under sunlight, propagate the
1144 sunlight down from it and then light all neighbors
1145 of the propagated blocks.
1147 if(node_under_sunlight)
1149 s16 ybottom = propagateSunlight(p, modified_blocks);
1150 /*m_dout<<DTIME<<"Node was under sunlight. "
1151 "Propagating sunlight";
1152 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1154 for(; y >= ybottom; y--)
1156 v3s16 p2(p.X, y, p.Z);
1157 /*m_dout<<DTIME<<"lighting neighbors of node ("
1158 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1160 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1165 // Set the lighting of this node to 0
1166 // TODO: Is this needed? Lighting is cleared up there already.
1168 MapNode n = getNode(p);
1169 n.setLight(LIGHTBANK_DAY, 0);
1172 catch(InvalidPositionException &e)
1178 for(s32 i=0; i<2; i++)
1180 enum LightBank bank = banks[i];
1182 // Get the brightest neighbour node and propagate light from it
1183 v3s16 n2p = getBrightestNeighbour(bank, p);
1185 MapNode n2 = getNode(n2p);
1186 lightNeighbors(bank, n2p, modified_blocks);
1188 catch(InvalidPositionException &e)
1194 Update information about whether day and night light differ
1196 for(core::map<v3s16, MapBlock*>::Iterator
1197 i = modified_blocks.getIterator();
1198 i.atEnd() == false; i++)
1200 MapBlock *block = i.getNode()->getValue();
1201 block->updateDayNightDiff();
1205 Add neighboring liquid nodes to transform queue.
1208 v3s16(0,0,1), // back
1209 v3s16(0,1,0), // top
1210 v3s16(1,0,0), // right
1211 v3s16(0,0,-1), // front
1212 v3s16(0,-1,0), // bottom
1213 v3s16(-1,0,0), // left
1215 for(u16 i=0; i<6; i++)
1220 v3s16 p2 = p + dirs[i];
1222 MapNode n2 = getNode(p2);
1223 if(content_liquid(n2.d))
1225 m_transforming_liquid.push_back(p2);
1228 }catch(InvalidPositionException &e)
1234 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1237 event.type = MEET_ADDNODE;
1241 bool succeeded = true;
1243 core::map<v3s16, MapBlock*> modified_blocks;
1244 addNodeAndUpdate(p, n, modified_blocks);
1246 // Copy modified_blocks to event
1247 for(core::map<v3s16, MapBlock*>::Iterator
1248 i = modified_blocks.getIterator();
1249 i.atEnd()==false; i++)
1251 event.modified_blocks.insert(i.getNode()->getKey(), false);
1254 catch(InvalidPositionException &e){
1258 dispatchEvent(&event);
1263 bool Map::removeNodeWithEvent(v3s16 p)
1266 event.type = MEET_REMOVENODE;
1269 bool succeeded = true;
1271 core::map<v3s16, MapBlock*> modified_blocks;
1272 removeNodeAndUpdate(p, modified_blocks);
1274 // Copy modified_blocks to event
1275 for(core::map<v3s16, MapBlock*>::Iterator
1276 i = modified_blocks.getIterator();
1277 i.atEnd()==false; i++)
1279 event.modified_blocks.insert(i.getNode()->getKey(), false);
1282 catch(InvalidPositionException &e){
1286 dispatchEvent(&event);
1291 bool Map::dayNightDiffed(v3s16 blockpos)
1294 v3s16 p = blockpos + v3s16(0,0,0);
1295 MapBlock *b = getBlockNoCreate(p);
1296 if(b->dayNightDiffed())
1299 catch(InvalidPositionException &e){}
1302 v3s16 p = blockpos + v3s16(-1,0,0);
1303 MapBlock *b = getBlockNoCreate(p);
1304 if(b->dayNightDiffed())
1307 catch(InvalidPositionException &e){}
1309 v3s16 p = blockpos + v3s16(0,-1,0);
1310 MapBlock *b = getBlockNoCreate(p);
1311 if(b->dayNightDiffed())
1314 catch(InvalidPositionException &e){}
1316 v3s16 p = blockpos + v3s16(0,0,-1);
1317 MapBlock *b = getBlockNoCreate(p);
1318 if(b->dayNightDiffed())
1321 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(1,0,0);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1331 v3s16 p = blockpos + v3s16(0,1,0);
1332 MapBlock *b = getBlockNoCreate(p);
1333 if(b->dayNightDiffed())
1336 catch(InvalidPositionException &e){}
1338 v3s16 p = blockpos + v3s16(0,0,1);
1339 MapBlock *b = getBlockNoCreate(p);
1340 if(b->dayNightDiffed())
1343 catch(InvalidPositionException &e){}
1349 Updates usage timers
1351 void Map::timerUpdate(float dtime)
1353 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1355 core::map<v2s16, MapSector*>::Iterator si;
1357 si = m_sectors.getIterator();
1358 for(; si.atEnd() == false; si++)
1360 MapSector *sector = si.getNode()->getValue();
1362 core::list<MapBlock*> blocks;
1363 sector->getBlocks(blocks);
1364 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1365 i != blocks.end(); i++)
1367 (*i)->incrementUsageTimer(dtime);
1372 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1374 core::list<v2s16>::Iterator j;
1375 for(j=list.begin(); j!=list.end(); j++)
1377 MapSector *sector = m_sectors[*j];
1380 sector->deleteBlocks();
1385 If sector is in sector cache, remove it from there
1387 if(m_sector_cache == sector)
1389 m_sector_cache = NULL;
1392 Remove from map and delete
1394 m_sectors.remove(*j);
1400 u32 Map::unloadUnusedData(float timeout, bool only_blocks,
1401 core::list<v3s16> *deleted_blocks)
1403 core::list<v2s16> sector_deletion_queue;
1405 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1406 for(; si.atEnd() == false; si++)
1408 MapSector *sector = si.getNode()->getValue();
1410 bool all_blocks_deleted = true;
1412 core::list<MapBlock*> blocks;
1413 sector->getBlocks(blocks);
1414 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1415 i != blocks.end(); i++)
1417 MapBlock *block = (*i);
1419 if(block->getUsageTimer() > timeout)
1422 if(block->getModified() != MOD_STATE_CLEAN)
1425 sector->removeBlock(block);
1430 all_blocks_deleted = false;
1434 if(all_blocks_deleted)
1436 sector_deletion_queue.push_back(si.getNode()->getKey());
1441 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1442 for(; i.atEnd() == false; i++)
1444 MapSector *sector = i.getNode()->getValue();
1446 Delete sector from memory if it hasn't been used in a long time
1448 if(sector->usage_timer > timeout)
1450 sector_deletion_queue.push_back(i.getNode()->getKey());
1452 if(deleted_blocks != NULL)
1454 // Collect positions of blocks of sector
1455 MapSector *sector = i.getNode()->getValue();
1456 core::list<MapBlock*> blocks;
1457 sector->getBlocks(blocks);
1458 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1459 i != blocks.end(); i++)
1461 deleted_blocks->push_back((*i)->getPos());
1468 deleteSectors(sector_deletion_queue, only_blocks);
1469 return sector_deletion_queue.getSize();
1472 void Map::PrintInfo(std::ostream &out)
1477 #define WATER_DROP_BOOST 4
1479 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1481 DSTACK(__FUNCTION_NAME);
1482 //TimeTaker timer("transformLiquids()");
1485 u32 initial_size = m_transforming_liquid.size();
1487 /*if(initial_size != 0)
1488 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1490 while(m_transforming_liquid.size() != 0)
1493 Get a queued transforming liquid node
1495 v3s16 p0 = m_transforming_liquid.pop_front();
1497 MapNode n0 = getNode(p0);
1499 // Don't deal with non-liquids
1500 if(content_liquid(n0.d) == false)
1503 bool is_source = !content_flowing_liquid(n0.d);
1505 u8 liquid_level = 8;
1506 if(is_source == false)
1507 liquid_level = n0.param2 & 0x0f;
1509 // Turn possible source into non-source
1510 u8 nonsource_c = make_liquid_flowing(n0.d);
1513 If not source, check that some node flows into this one
1514 and what is the level of liquid in this one
1516 if(is_source == false)
1518 s8 new_liquid_level_max = -1;
1520 v3s16 dirs_from[5] = {
1521 v3s16(0,1,0), // top
1522 v3s16(0,0,1), // back
1523 v3s16(1,0,0), // right
1524 v3s16(0,0,-1), // front
1525 v3s16(-1,0,0), // left
1527 for(u16 i=0; i<5; i++)
1532 bool from_top = (i==0);
1534 v3s16 p2 = p0 + dirs_from[i];
1535 MapNode n2 = getNode(p2);
1537 if(content_liquid(n2.d))
1539 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1540 // Check that the liquids are the same type
1541 if(n2_nonsource_c != nonsource_c)
1543 dstream<<"WARNING: Not handling: different liquids"
1544 " collide"<<std::endl;
1547 bool n2_is_source = !content_flowing_liquid(n2.d);
1548 s8 n2_liquid_level = 8;
1549 if(n2_is_source == false)
1550 n2_liquid_level = n2.param2 & 0x07;
1552 s8 new_liquid_level = -1;
1555 //new_liquid_level = 7;
1556 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1557 new_liquid_level = 7;
1559 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1561 else if(n2_liquid_level > 0)
1563 new_liquid_level = n2_liquid_level - 1;
1566 if(new_liquid_level > new_liquid_level_max)
1567 new_liquid_level_max = new_liquid_level;
1570 }catch(InvalidPositionException &e)
1576 If liquid level should be something else, update it and
1577 add all the neighboring water nodes to the transform queue.
1579 if(new_liquid_level_max != liquid_level)
1581 if(new_liquid_level_max == -1)
1583 // Remove water alltoghether
1590 n0.param2 = new_liquid_level_max;
1594 // Block has been modified
1596 v3s16 blockpos = getNodeBlockPos(p0);
1597 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1599 modified_blocks.insert(blockpos, block);
1603 Add neighboring non-source liquid nodes to transform queue.
1606 v3s16(0,0,1), // back
1607 v3s16(0,1,0), // top
1608 v3s16(1,0,0), // right
1609 v3s16(0,0,-1), // front
1610 v3s16(0,-1,0), // bottom
1611 v3s16(-1,0,0), // left
1613 for(u16 i=0; i<6; i++)
1618 v3s16 p2 = p0 + dirs[i];
1620 MapNode n2 = getNode(p2);
1621 if(content_flowing_liquid(n2.d))
1623 m_transforming_liquid.push_back(p2);
1626 }catch(InvalidPositionException &e)
1633 // Get a new one from queue if the node has turned into non-water
1634 if(content_liquid(n0.d) == false)
1638 Flow water from this node
1640 v3s16 dirs_to[5] = {
1641 v3s16(0,-1,0), // bottom
1642 v3s16(0,0,1), // back
1643 v3s16(1,0,0), // right
1644 v3s16(0,0,-1), // front
1645 v3s16(-1,0,0), // left
1647 for(u16 i=0; i<5; i++)
1652 bool to_bottom = (i == 0);
1654 // If liquid is at lowest possible height, it's not going
1655 // anywhere except down
1656 if(liquid_level == 0 && to_bottom == false)
1659 u8 liquid_next_level = 0;
1660 // If going to bottom
1663 //liquid_next_level = 7;
1664 if(liquid_level >= 7 - WATER_DROP_BOOST)
1665 liquid_next_level = 7;
1667 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1670 liquid_next_level = liquid_level - 1;
1672 bool n2_changed = false;
1673 bool flowed = false;
1675 v3s16 p2 = p0 + dirs_to[i];
1677 MapNode n2 = getNode(p2);
1678 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1680 if(content_liquid(n2.d))
1682 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1683 // Check that the liquids are the same type
1684 if(n2_nonsource_c != nonsource_c)
1686 dstream<<"WARNING: Not handling: different liquids"
1687 " collide"<<std::endl;
1690 bool n2_is_source = !content_flowing_liquid(n2.d);
1691 u8 n2_liquid_level = 8;
1692 if(n2_is_source == false)
1693 n2_liquid_level = n2.param2 & 0x07;
1702 // Just flow into the source, nothing changes.
1703 // n2_changed is not set because destination didn't change
1708 if(liquid_next_level > liquid_level)
1710 n2.param2 = liquid_next_level;
1718 else if(n2.d == CONTENT_AIR)
1721 n2.param2 = liquid_next_level;
1728 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1732 m_transforming_liquid.push_back(p2);
1734 v3s16 blockpos = getNodeBlockPos(p2);
1735 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1737 modified_blocks.insert(blockpos, block);
1740 // If n2_changed to bottom, don't flow anywhere else
1741 if(to_bottom && flowed && !is_source)
1744 }catch(InvalidPositionException &e)
1750 //if(loopcount >= 100000)
1751 if(loopcount >= initial_size * 1)
1754 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1757 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1759 v3s16 blockpos = getNodeBlockPos(p);
1760 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1761 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1764 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1768 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1772 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1774 v3s16 blockpos = getNodeBlockPos(p);
1775 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1776 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1779 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1783 block->m_node_metadata.set(p_rel, meta);
1786 void Map::removeNodeMetadata(v3s16 p)
1788 v3s16 blockpos = getNodeBlockPos(p);
1789 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1790 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1793 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1797 block->m_node_metadata.remove(p_rel);
1800 void Map::nodeMetadataStep(float dtime,
1801 core::map<v3s16, MapBlock*> &changed_blocks)
1805 Currently there is no way to ensure that all the necessary
1806 blocks are loaded when this is run. (They might get unloaded)
1807 NOTE: ^- Actually, that might not be so. In a quick test it
1808 reloaded a block with a furnace when I walked back to it from
1811 core::map<v2s16, MapSector*>::Iterator si;
1812 si = m_sectors.getIterator();
1813 for(; si.atEnd() == false; si++)
1815 MapSector *sector = si.getNode()->getValue();
1816 core::list< MapBlock * > sectorblocks;
1817 sector->getBlocks(sectorblocks);
1818 core::list< MapBlock * >::Iterator i;
1819 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1821 MapBlock *block = *i;
1822 bool changed = block->m_node_metadata.step(dtime);
1824 changed_blocks[block->getPos()] = block;
1833 ServerMap::ServerMap(std::string savedir):
1836 m_map_metadata_changed(true)
1838 dstream<<__FUNCTION_NAME<<std::endl;
1840 //m_chunksize = 8; // Takes a few seconds
1842 m_seed = (((u64)(myrand()%0xffff)<<0)
1843 + ((u64)(myrand()%0xffff)<<16)
1844 + ((u64)(myrand()%0xffff)<<32)
1845 + ((u64)(myrand()%0xffff)<<48));
1848 Experimental and debug stuff
1855 Try to load map; if not found, create a new one.
1858 m_savedir = savedir;
1859 m_map_saving_enabled = false;
1863 // If directory exists, check contents and load if possible
1864 if(fs::PathExists(m_savedir))
1866 // If directory is empty, it is safe to save into it.
1867 if(fs::GetDirListing(m_savedir).size() == 0)
1869 dstream<<DTIME<<"Server: Empty save directory is valid."
1871 m_map_saving_enabled = true;
1876 // Load map metadata (seed, chunksize)
1879 catch(FileNotGoodException &e){
1880 dstream<<DTIME<<"WARNING: Could not load map metadata"
1881 //<<" Disabling chunk-based generator."
1887 // Load chunk metadata
1890 catch(FileNotGoodException &e){
1891 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1892 <<" Disabling chunk-based generator."
1897 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1898 "metadata and sector (0,0) from "<<savedir<<
1899 ", assuming valid save directory."
1902 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1903 <<"and chunk metadata from "<<savedir
1904 <<", assuming valid save directory."
1907 m_map_saving_enabled = true;
1908 // Map loaded, not creating new one
1912 // If directory doesn't exist, it is safe to save to it
1914 m_map_saving_enabled = true;
1917 catch(std::exception &e)
1919 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1920 <<", exception: "<<e.what()<<std::endl;
1921 dstream<<"Please remove the map or fix it."<<std::endl;
1922 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1925 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1927 // Create zero sector
1928 emergeSector(v2s16(0,0));
1930 // Initially write whole map
1934 ServerMap::~ServerMap()
1936 dstream<<__FUNCTION_NAME<<std::endl;
1940 if(m_map_saving_enabled)
1943 // Save only changed parts
1945 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1949 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1952 catch(std::exception &e)
1954 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1955 <<", exception: "<<e.what()<<std::endl;
1962 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1963 for(; i.atEnd() == false; i++)
1965 MapChunk *chunk = i.getNode()->getValue();
1972 Some helper functions for the map generator
1975 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1977 v3s16 em = vmanip.m_area.getExtent();
1978 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1979 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1980 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1982 for(y=y_nodes_max; y>=y_nodes_min; y--)
1984 MapNode &n = vmanip.m_data[i];
1985 if(content_walkable(n.d))
1988 vmanip.m_area.add_y(em, i, -1);
1990 if(y >= y_nodes_min)
1996 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1998 v3s16 em = vmanip.m_area.getExtent();
1999 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2000 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2001 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2003 for(y=y_nodes_max; y>=y_nodes_min; y--)
2005 MapNode &n = vmanip.m_data[i];
2006 if(content_walkable(n.d)
2007 && n.d != CONTENT_TREE
2008 && n.d != CONTENT_LEAVES)
2011 vmanip.m_area.add_y(em, i, -1);
2013 if(y >= y_nodes_min)
2019 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2021 MapNode treenode(CONTENT_TREE);
2022 MapNode leavesnode(CONTENT_LEAVES);
2024 s16 trunk_h = myrand_range(3, 6);
2026 for(s16 ii=0; ii<trunk_h; ii++)
2028 if(vmanip.m_area.contains(p1))
2029 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2033 // p1 is now the last piece of the trunk
2036 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2037 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2038 Buffer<u8> leaves_d(leaves_a.getVolume());
2039 for(s32 i=0; i<leaves_a.getVolume(); i++)
2042 // Force leaves at near the end of the trunk
2045 for(s16 z=-d; z<=d; z++)
2046 for(s16 y=-d; y<=d; y++)
2047 for(s16 x=-d; x<=d; x++)
2049 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2053 // Add leaves randomly
2054 for(u32 iii=0; iii<7; iii++)
2059 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2060 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2061 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2064 for(s16 z=0; z<=d; z++)
2065 for(s16 y=0; y<=d; y++)
2066 for(s16 x=0; x<=d; x++)
2068 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2072 // Blit leaves to vmanip
2073 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2074 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2075 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2079 if(vmanip.m_area.contains(p) == false)
2081 u32 vi = vmanip.m_area.index(p);
2082 if(vmanip.m_data[vi].d != CONTENT_AIR
2083 && vmanip.m_data[vi].d != CONTENT_IGNORE)
2085 u32 i = leaves_a.index(x,y,z);
2086 if(leaves_d[i] == 1)
2087 vmanip.m_data[vi] = leavesnode;
2092 void make_randomstone(VoxelManipulator &vmanip, v3s16 p0)
2094 MapNode stonenode(CONTENT_STONE);
2096 s16 size = myrand_range(3, 6);
2098 VoxelArea stone_a(v3s16(-2,0,-2), v3s16(2,size,2));
2099 Buffer<u8> stone_d(stone_a.getVolume());
2100 for(s32 i=0; i<stone_a.getVolume(); i++)
2103 // Force stone at bottom to make it usually touch the ground
2105 for(s16 z=0; z<=0; z++)
2106 for(s16 y=0; y<=0; y++)
2107 for(s16 x=0; x<=0; x++)
2109 stone_d[stone_a.index(v3s16(x,y,z))] = 1;
2113 // Generate from perlin noise
2114 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2115 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2116 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2118 double d = noise3d_perlin((float)x/3.,(float)z/3.,(float)y/3.,
2119 p0.Z*4243+p0.Y*34+p0.X, 2, 0.5);
2120 if(z == stone_a.MinEdge.Z || z == stone_a.MaxEdge.Z)
2122 if(/*y == stone_a.MinEdge.Y ||*/ y == stone_a.MaxEdge.Y)
2124 if(x == stone_a.MinEdge.X || x == stone_a.MaxEdge.X)
2128 u32 vi = stone_a.index(v3s16(x,y,z));
2133 /*// Add stone randomly
2134 for(u32 iii=0; iii<7; iii++)
2139 myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
2140 myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
2141 myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
2144 for(s16 z=0; z<=d; z++)
2145 for(s16 y=0; y<=d; y++)
2146 for(s16 x=0; x<=d; x++)
2148 stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
2152 // Blit stone to vmanip
2153 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2154 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2155 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2159 if(vmanip.m_area.contains(p) == false)
2161 u32 vi = vmanip.m_area.index(p);
2162 if(vmanip.m_data[vi].d != CONTENT_AIR
2163 && vmanip.m_data[vi].d != CONTENT_IGNORE)
2165 u32 i = stone_a.index(x,y,z);
2167 vmanip.m_data[vi] = stonenode;
2172 void make_largestone(VoxelManipulator &vmanip, v3s16 p0)
2174 MapNode stonenode(CONTENT_STONE);
2176 s16 size = myrand_range(8, 16);
2178 VoxelArea stone_a(v3s16(-size/2,0,-size/2), v3s16(size/2,size,size/2));
2179 Buffer<u8> stone_d(stone_a.getVolume());
2180 for(s32 i=0; i<stone_a.getVolume(); i++)
2183 // Force stone at bottom to make it usually touch the ground
2185 for(s16 z=0; z<=0; z++)
2186 for(s16 y=0; y<=0; y++)
2187 for(s16 x=0; x<=0; x++)
2189 stone_d[stone_a.index(v3s16(x,y,z))] = 1;
2193 // Generate from perlin noise
2194 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2195 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2196 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2199 d += noise3d_perlin((float)x/10.,(float)z/10.,(float)y/10.,
2200 p0.Z*5123+p0.Y*2439+p0.X, 2, 0.5);
2201 double mid_z = (stone_a.MaxEdge.Z+stone_a.MinEdge.Z)/2;
2202 double mid_x = (stone_a.MaxEdge.X+stone_a.MinEdge.X)/2;
2203 double mid_y = (stone_a.MaxEdge.Y+stone_a.MinEdge.Y)/2;
2204 double dz = (double)z-mid_z;
2205 double dx = (double)x-mid_x;
2206 double dy = MYMAX(0, (double)y-mid_y);
2207 double r = sqrt(dz*dz+dx*dx+dy*dy);
2208 d /= (2*r/size)*2 + 0.01;
2211 u32 vi = stone_a.index(v3s16(x,y,z));
2216 /*// Add stone randomly
2217 for(u32 iii=0; iii<7; iii++)
2222 myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
2223 myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
2224 myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
2227 for(s16 z=0; z<=d; z++)
2228 for(s16 y=0; y<=d; y++)
2229 for(s16 x=0; x<=d; x++)
2231 stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
2235 // Blit stone to vmanip
2236 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2237 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2238 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2242 if(vmanip.m_area.contains(p) == false)
2244 u32 vi = vmanip.m_area.index(p);
2245 /*if(vmanip.m_data[vi].d != CONTENT_AIR
2246 && vmanip.m_data[vi].d != CONTENT_IGNORE)
2248 u32 i = stone_a.index(x,y,z);
2250 vmanip.m_data[vi] = stonenode;
2255 Dungeon making routines
2258 #define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
2259 #define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
2260 #define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
2261 VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
2263 void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace)
2266 for(s16 z=0; z<roomsize.Z; z++)
2267 for(s16 y=0; y<roomsize.Y; y++)
2270 v3s16 p = roomplace + v3s16(0,y,z);
2271 if(vmanip.m_area.contains(p) == false)
2273 u32 vi = vmanip.m_area.index(p);
2274 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2276 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2279 v3s16 p = roomplace + v3s16(roomsize.X-1,y,z);
2280 if(vmanip.m_area.contains(p) == false)
2282 u32 vi = vmanip.m_area.index(p);
2283 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2285 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2290 for(s16 x=0; x<roomsize.X; x++)
2291 for(s16 y=0; y<roomsize.Y; y++)
2294 v3s16 p = roomplace + v3s16(x,y,0);
2295 if(vmanip.m_area.contains(p) == false)
2297 u32 vi = vmanip.m_area.index(p);
2298 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2300 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2303 v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1);
2304 if(vmanip.m_area.contains(p) == false)
2306 u32 vi = vmanip.m_area.index(p);
2307 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2309 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2313 // Make +-Y walls (floor and ceiling)
2314 for(s16 z=0; z<roomsize.Z; z++)
2315 for(s16 x=0; x<roomsize.X; x++)
2318 v3s16 p = roomplace + v3s16(x,0,z);
2319 if(vmanip.m_area.contains(p) == false)
2321 u32 vi = vmanip.m_area.index(p);
2322 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2324 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2327 v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z);
2328 if(vmanip.m_area.contains(p) == false)
2330 u32 vi = vmanip.m_area.index(p);
2331 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2333 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2338 for(s16 z=1; z<roomsize.Z-1; z++)
2339 for(s16 y=1; y<roomsize.Y-1; y++)
2340 for(s16 x=1; x<roomsize.X-1; x++)
2342 v3s16 p = roomplace + v3s16(x,y,z);
2343 if(vmanip.m_area.contains(p) == false)
2345 u32 vi = vmanip.m_area.index(p);
2346 vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
2347 vmanip.m_data[vi] = MapNode(CONTENT_AIR);
2351 void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size,
2352 u8 avoid_flags, MapNode n, u8 or_flags)
2354 for(s16 z=0; z<size.Z; z++)
2355 for(s16 y=0; y<size.Y; y++)
2356 for(s16 x=0; x<size.X; x++)
2358 v3s16 p = place + v3s16(x,y,z);
2359 if(vmanip.m_area.contains(p) == false)
2361 u32 vi = vmanip.m_area.index(p);
2362 if(vmanip.m_flags[vi] & avoid_flags)
2364 vmanip.m_flags[vi] |= or_flags;
2365 vmanip.m_data[vi] = n;
2369 void make_hole1(VoxelManipulator &vmanip, v3s16 place)
2371 make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
2372 VMANIP_FLAG_DUNGEON_INSIDE);
2375 void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir)
2377 make_hole1(vmanip, doorplace);
2380 v3s16 rand_ortho_dir()
2383 return myrand()%2 ? v3s16(-1,0,0) : v3s16(1,0,0);
2385 return myrand()%2 ? v3s16(0,0,-1) : v3s16(0,0,1);
2388 void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir,
2389 v3s16 &result_place, v3s16 &result_dir)
2391 make_hole1(vmanip, doorplace);
2392 v3s16 p0 = doorplace;
2393 v3s16 dir = doordir;
2396 length = myrand_range(1,13);
2398 length = myrand_range(1,6);
2399 u32 partlength = myrand_range(1,length);
2401 s16 make_stairs = 0;
2402 if(myrand()%2 == 0 && partlength >= 3)
2403 make_stairs = myrand()%2 ? 1 : -1;
2404 for(u32 i=0; i<length; i++)
2407 // If first part of stairs, don't go up or down yet (wouldn't fit)
2410 v3s16 p = p0 + dir0;
2411 /*// If already empty
2412 if(vmanip.getNodeNoExNoEmerge(p).d
2414 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2420 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3),
2421 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(CONTENT_COBBLE), 0);
2422 make_fill(vmanip, p, v3s16(1,3,1), 0, MapNode(CONTENT_AIR),
2423 VMANIP_FLAG_DUNGEON_INSIDE);
2427 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3),
2428 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(CONTENT_COBBLE), 0);
2429 make_hole1(vmanip, p);
2430 /*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
2431 VMANIP_FLAG_DUNGEON_INSIDE);*/
2435 if(partcount >= partlength)
2439 v3s16 newdir = rand_ortho_dir();
2440 partlength = myrand_range(1,7);
2442 if(myrand()%2 == 0 && partlength >= 3)
2443 make_stairs = myrand()%2 ? 1 : -1;
2445 if(make_stairs != 0)
2447 if(newdir.X == 0 && dir.X != 0)
2449 if(newdir.Z == 0 && dir.Z != 0)
2456 dir.Y = make_stairs;
2459 //p0.Y -= make_stairs;
2469 RoomWalker(VoxelManipulator &vmanip_, v3s16 pos):
2478 m_dir = rand_ortho_dir();
2481 void setPos(v3s16 pos)
2486 void setDir(v3s16 dir)
2491 bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
2493 for(u32 i=0; i<100; i++)
2495 v3s16 p = m_pos + m_dir;
2496 v3s16 p1 = p + v3s16(0,1,0);
2497 if(vmanip.m_area.contains(p) == false
2498 || vmanip.m_area.contains(p1) == false
2504 if(vmanip.getNodeNoExNoEmerge(p).d
2506 && vmanip.getNodeNoExNoEmerge(p1).d
2509 // Found wall, this is a good place!
2512 // Randomize next direction
2517 Determine where to move next
2519 // Jump one up if the actual space is there
2520 if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
2522 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2524 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).d
2527 // Jump one down if the actual space is there
2528 if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2530 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
2532 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).d
2535 // Check if walking is now possible
2536 if(vmanip.getNodeNoExNoEmerge(p).d
2538 || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2541 // Cannot continue walking here
2551 bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
2552 v3s16 &result_doordir, v3s16 &result_roomplace)
2554 for(s16 trycount=0; trycount<30; trycount++)
2558 bool r = findPlaceForDoor(doorplace, doordir);
2562 // X east, Z north, Y up
2563 if(doordir == v3s16(1,0,0)) // X+
2564 roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+myrand_range(-roomsize.Z/2,roomsize.Z/2));
2565 if(doordir == v3s16(-1,0,0)) // X-
2566 roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+myrand_range(-roomsize.Z/2,roomsize.Z/2));
2567 if(doordir == v3s16(0,0,1)) // Z+
2568 roomplace = doorplace + v3s16(-roomsize.X/2+myrand_range(-roomsize.X/2,roomsize.X/2),-1,0);
2569 if(doordir == v3s16(0,0,-1)) // Z-
2570 roomplace = doorplace + v3s16(-roomsize.X/2+myrand_range(-roomsize.X/2,roomsize.X/2),-1,-roomsize.Z+1);
2574 for(s16 z=1; z<roomsize.Z-1; z++)
2575 for(s16 y=1; y<roomsize.Y-1; y++)
2576 for(s16 x=1; x<roomsize.X-1; x++)
2578 v3s16 p = roomplace + v3s16(x,y,z);
2579 if(vmanip.m_area.contains(p) == false)
2584 if(vmanip.m_flags[vmanip.m_area.index(p)]
2585 & VMANIP_FLAG_DUNGEON_INSIDE)
2596 result_doorplace = doorplace;
2597 result_doordir = doordir;
2598 result_roomplace = roomplace;
2605 VoxelManipulator &vmanip;
2610 void make_dungeon1(VoxelManipulator &vmanip)
2612 v3s16 areasize = vmanip.m_area.getExtent();
2616 roomsize = v3s16(myrand_range(4,8),myrand_range(4,6),myrand_range(4,8));
2617 roomplace = vmanip.m_area.MinEdge + v3s16(
2618 myrand_range(0,areasize.X-roomsize.X-1),
2619 myrand_range(0,areasize.Y-roomsize.Y-1),
2620 myrand_range(0,areasize.Z-roomsize.Z-1));
2622 u32 room_count = myrand_range(2,7);
2623 for(u32 i=0; i<room_count; i++)
2625 make_room1(vmanip, roomsize, roomplace);
2627 RoomWalker walker(vmanip, roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2));
2630 bool r = walker.findPlaceForDoor(doorplace, doordir);
2633 //dstream<<"walker.findPlaceForDoor failed"<<std::endl;
2636 //dstream<<"walker.findPlaceForDoor succeeded"<<std::endl;
2638 make_door1(vmanip, doorplace, doordir);
2641 v3s16 corridor_end_dir;
2642 make_corridor(vmanip, doorplace, doordir, corridor_end, corridor_end_dir);
2644 roomsize = v3s16(myrand_range(4,8),myrand_range(4,6),myrand_range(4,8));
2645 walker.setPos(corridor_end);
2646 walker.setDir(corridor_end_dir);
2647 r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
2650 //dstream<<"walker.findPlaceForRoomDoor failed"<<std::endl;
2653 //dstream<<"walker.findPlaceForRoomDoor succeeded"<<std::endl;
2654 make_door1(vmanip, doorplace, doordir);
2657 make_room1(vmanip, roomsize, roomplace);
2662 Noise functions. Make sure seed is mangled differently in each one.
2665 // This affects the shape of the contour
2666 //#define CAVE_NOISE_SCALE 10.0
2667 #define CAVE_NOISE_SCALE 5.0
2669 NoiseParams get_cave_noise1_params(u64 seed)
2671 /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
2672 200, CAVE_NOISE_SCALE);*/
2673 return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7,
2674 100, CAVE_NOISE_SCALE);
2677 NoiseParams get_cave_noise2_params(u64 seed)
2679 /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
2680 200, CAVE_NOISE_SCALE);*/
2681 return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7,
2682 100, CAVE_NOISE_SCALE);
2685 //#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
2686 #define CAVE_NOISE_THRESHOLD (2.0/CAVE_NOISE_SCALE)
2688 NoiseParams get_ground_noise1_params(u64 seed)
2690 return NoiseParams(NOISE_PERLIN, seed+983240, 5,
2694 NoiseParams get_ground_crumbleness_params(u64 seed)
2696 return NoiseParams(NOISE_PERLIN, seed+34413, 3,
2700 NoiseParams get_ground_wetness_params(u64 seed)
2702 return NoiseParams(NOISE_PERLIN, seed+32474, 4,
2706 bool is_cave(u64 seed, v3s16 p)
2708 double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z);
2709 double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z);
2710 return d1*d2 > CAVE_NOISE_THRESHOLD;
2714 Ground density noise shall be interpreted by using this.
2716 TODO: No perlin noises here, they should be outsourced
2719 bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
2721 //return ((double)p.Y < ground_noise1_val);
2723 double f = 1.0 + noise2d_perlin(
2724 0.5+(float)p.X/250, 0.5+(float)p.Z/250,
2725 seed+920381, 3, 0.5);
2730 double h = WATER_LEVEL + 10 * noise2d_perlin(
2731 0.5+(float)p.X/250, 0.5+(float)p.Z/250,
2732 seed+84174, 4, 0.5);
2733 return ((double)p.Y - h < ground_noise1_val * f);
2737 Queries whether a position is ground or not.
2739 bool is_ground(u64 seed, v3s16 p)
2741 double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z);
2742 return val_is_ground(val1, p, seed);
2745 // Amount of trees per area in nodes
2746 double tree_amount_2d(u64 seed, v2s16 p)
2748 /*double noise = noise2d_perlin(
2749 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2751 double noise = noise2d_perlin(
2752 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
2754 double zeroval = -0.35;
2758 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2762 double randomstone_amount_2d(u64 seed, v2s16 p)
2764 double noise = noise2d_perlin(
2765 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2766 seed+3829434, 5, 0.66);
2767 double zeroval = 0.1;
2771 return 0.01 * (noise-zeroval) / (1.0-zeroval);
2775 double largestone_amount_2d(u64 seed, v2s16 p)
2777 double noise = noise2d_perlin(
2778 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2779 seed+14143242, 5, 0.66);
2780 double zeroval = 0.3;
2784 return 0.005 * (noise-zeroval) / (1.0-zeroval);
2788 Incrementally find ground level from 3d noise
2790 s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
2792 // Start a bit fuzzy to make averaging lower precision values
2794 s16 level = myrand_range(-precision/2, precision/2);
2795 s16 dec[] = {31000, 100, 20, 4, 1, 0};
2797 for(i = 1; dec[i] != 0 && precision <= dec[i]; i++)
2799 // First find non-ground by going upwards
2800 // Don't stop in caves.
2802 s16 max = level+dec[i-1]*2;
2803 v3s16 p(p2d.X, level, p2d.Y);
2804 for(; p.Y < max; p.Y += dec[i])
2806 if(!is_ground(seed, p))
2813 // Then find ground by going downwards from there.
2814 // Go in caves, too, when precision is 1.
2816 s16 min = level-dec[i-1]*2;
2817 v3s16 p(p2d.X, level, p2d.Y);
2818 for(; p.Y>min; p.Y-=dec[i])
2820 bool ground = is_ground(seed, p);
2821 /*if(dec[i] == 1 && is_cave(seed, p))
2832 // This is more like the actual ground level
2833 level += dec[i-1]/2;
2838 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4);
2840 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p)
2842 v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
2843 v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
2845 a += find_ground_level_from_noise(seed,
2846 v2s16(node_min.X, node_min.Y), p);
2847 a += find_ground_level_from_noise(seed,
2848 v2s16(node_min.X, node_max.Y), p);
2849 a += find_ground_level_from_noise(seed,
2850 v2s16(node_max.X, node_max.Y), p);
2851 a += find_ground_level_from_noise(seed,
2852 v2s16(node_max.X, node_min.Y), p);
2853 a += find_ground_level_from_noise(seed,
2854 v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p);
2859 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
2861 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
2863 v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
2864 v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
2866 a = MYMAX(a, find_ground_level_from_noise(seed,
2867 v2s16(node_min.X, node_min.Y), p));
2868 a = MYMAX(a, find_ground_level_from_noise(seed,
2869 v2s16(node_min.X, node_max.Y), p));
2870 a = MYMAX(a, find_ground_level_from_noise(seed,
2871 v2s16(node_max.X, node_max.Y), p));
2872 a = MYMAX(a, find_ground_level_from_noise(seed,
2873 v2s16(node_min.X, node_min.Y), p));
2874 a = MYMAX(a, find_ground_level_from_noise(seed,
2875 v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
2879 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
2881 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
2883 v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
2884 v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
2886 a = MYMIN(a, find_ground_level_from_noise(seed,
2887 v2s16(node_min.X, node_min.Y), p));
2888 a = MYMIN(a, find_ground_level_from_noise(seed,
2889 v2s16(node_min.X, node_max.Y), p));
2890 a = MYMIN(a, find_ground_level_from_noise(seed,
2891 v2s16(node_max.X, node_max.Y), p));
2892 a = MYMIN(a, find_ground_level_from_noise(seed,
2893 v2s16(node_min.X, node_min.Y), p));
2894 a = MYMIN(a, find_ground_level_from_noise(seed,
2895 v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
2900 #define AVERAGE_MUD_AMOUNT 4
2902 double base_rock_level_2d(u64 seed, v2s16 p)
2904 // The base ground level
2905 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2906 + 20. * noise2d_perlin(
2907 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2908 (seed>>32)+654879876, 6, 0.6);
2910 /*// A bit hillier one
2911 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2912 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2913 (seed>>27)+90340, 6, 0.69);
2917 // Higher ground level
2918 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2919 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2920 seed+85039, 5, 0.69);
2921 //higher = 30; // For debugging
2923 // Limit higher to at least base
2927 // Steepness factor of cliffs
2928 double b = 1.0 + 1.0 * noise2d_perlin(
2929 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2931 b = rangelim(b, 0.0, 1000.0);
2934 b = rangelim(b, 3.0, 1000.0);
2935 //dstream<<"b="<<b<<std::endl;
2938 // Offset to more low
2939 double a_off = -0.2;
2940 // High/low selector
2941 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2942 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2943 seed-359, 6, 0.7));*/
2944 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2945 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2946 seed-359, 5, 0.60));
2948 a = rangelim(a, 0.0, 1.0);
2950 //dstream<<"a="<<a<<std::endl;
2952 double h = base*(1.0-a) + higher*a;
2959 double get_mud_add_amount(u64 seed, v2s16 p)
2961 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2962 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2963 seed+91013, 3, 0.55));
2967 bool get_have_sand(u64 seed, v2s16 p2d)
2969 // Determine whether to have sand here
2970 double sandnoise = noise2d_perlin(
2971 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
2972 seed+59420, 3, 0.50);
2974 return (sandnoise > -0.15);
2978 Adds random objects to block, depending on the content of the block
2980 void addRandomObjects(MapBlock *block)
2982 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2983 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2985 bool last_node_walkable = false;
2986 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2989 MapNode n = block->getNodeNoEx(p);
2990 if(n.d == CONTENT_IGNORE)
2992 if(content_features(n.d).liquid_type != LIQUID_NONE)
2994 if(content_features(n.d).walkable)
2996 last_node_walkable = true;
2999 if(last_node_walkable)
3001 // If block contains light information
3002 if(content_features(n.d).param_type == CPT_LIGHT)
3004 if(n.getLight(LIGHTBANK_DAY) <= 3)
3006 if(myrand() % 300 == 0)
3008 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
3010 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
3011 std::string data = obj->getStaticData();
3012 StaticObject s_obj(obj->getType(),
3013 obj->getBasePosition(), data);
3015 block->m_static_objects.insert(0, s_obj);
3016 block->m_static_objects.insert(0, s_obj);
3017 block->m_static_objects.insert(0, s_obj);
3018 block->m_static_objects.insert(0, s_obj);
3019 block->m_static_objects.insert(0, s_obj);
3020 block->m_static_objects.insert(0, s_obj);
3023 if(myrand() % 300 == 0)
3025 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
3027 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
3028 std::string data = obj->getStaticData();
3029 StaticObject s_obj(obj->getType(),
3030 obj->getBasePosition(), data);
3032 block->m_static_objects.insert(0, s_obj);
3038 last_node_walkable = false;
3041 block->setChangedFlag();
3044 void makeBlock(BlockMakeData *data)
3048 dstream<<"makeBlock: no-op"<<std::endl;
3052 v3s16 blockpos = data->blockpos;
3054 /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
3055 <<blockpos.Z<<")"<<std::endl;*/
3057 ManualMapVoxelManipulator &vmanip = data->vmanip;
3058 v3s16 blockpos_min = blockpos - v3s16(1,1,1);
3059 v3s16 blockpos_max = blockpos + v3s16(1,1,1);
3060 // Area of center block
3061 v3s16 node_min = blockpos*MAP_BLOCKSIZE;
3062 v3s16 node_max = (blockpos+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
3063 // Full allocated area
3064 v3s16 full_node_min = (blockpos-1)*MAP_BLOCKSIZE;
3065 v3s16 full_node_max = (blockpos+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
3067 double block_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE;
3069 v2s16 p2d_center(node_min.X+MAP_BLOCKSIZE/2, node_min.Z+MAP_BLOCKSIZE/2);
3072 Get average ground level from noise
3075 s16 approx_groundlevel = (s16)get_sector_average_ground_level(
3076 data->seed, v2s16(blockpos.X, blockpos.Z));
3077 //dstream<<"approx_groundlevel="<<approx_groundlevel<<std::endl;
3079 s16 approx_ground_depth = approx_groundlevel - (node_min.Y+MAP_BLOCKSIZE/2);
3081 /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
3082 data->seed, v2s16(blockpos.X, blockpos.Z));
3083 // Minimum amount of ground above the central block
3084 s16 minimum_ground_depth = minimum_groundlevel - node_max.Y;*/
3086 s16 maximum_groundlevel = (s16)get_sector_maximum_ground_level(
3087 data->seed, v2s16(blockpos.X, blockpos.Z), 1);
3088 // Minimum amount of ground above the central block
3089 s16 maximum_ground_depth = maximum_groundlevel - node_min.Y;
3092 Special case for high air or water
3094 if(maximum_ground_depth < -20)
3096 for(s16 x=node_min.X; x<=node_max.X; x++)
3097 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3102 // Use fast index incrementing
3103 v3s16 em = vmanip.m_area.getExtent();
3104 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
3105 for(s16 y=node_min.Y; y<=node_max.Y; y++)
3107 // Only modify places that have no content
3108 if(vmanip.m_data[i].d == CONTENT_IGNORE)
3110 if(y <= WATER_LEVEL)
3111 vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
3113 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3116 data->vmanip.m_area.add_y(em, i, 1);
3129 //NoiseBuffer noisebuf1;
3130 //NoiseBuffer noisebuf2;
3131 NoiseBuffer noisebuf_cave;
3132 NoiseBuffer noisebuf_ground;
3133 NoiseBuffer noisebuf_ground_crumbleness;
3134 NoiseBuffer noisebuf_ground_wetness;
3136 v3f minpos_f(node_min.X, node_min.Y, node_min.Z);
3137 v3f maxpos_f(node_max.X, node_max.Y, node_max.Z);
3139 //TimeTaker timer("noisebuf.create");
3145 noisebuf_cave.create(get_cave_noise1_params(data->seed),
3146 minpos_f.X, minpos_f.Y, minpos_f.Z,
3147 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
3151 noisebuf_cave.multiply(get_cave_noise2_params(data->seed));
3158 v3f sl = v3f(4.0, 4.0, 4.0);
3163 //noisebuf_ground.create(data->seed+983240, 6, 0.60, false,
3164 noisebuf_ground.create(get_ground_noise1_params(data->seed),
3165 minpos_f.X, minpos_f.Y, minpos_f.Z,
3166 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
3172 sl = v3f(2.5, 2.5, 2.5);
3173 noisebuf_ground_crumbleness.create(
3174 get_ground_crumbleness_params(data->seed),
3175 minpos_f.X, minpos_f.Y, minpos_f.Z,
3176 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
3178 noisebuf_ground_wetness.create(
3179 get_ground_wetness_params(data->seed),
3180 minpos_f.X, minpos_f.Y, minpos_f.Z,
3181 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
3186 Make base ground level
3189 for(s16 x=node_min.X; x<=node_max.X; x++)
3190 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3195 // Use fast index incrementing
3196 v3s16 em = vmanip.m_area.getExtent();
3197 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
3198 for(s16 y=node_min.Y; y<=node_max.Y; y++)
3200 // Only modify places that have no content
3201 if(vmanip.m_data[i].d == CONTENT_IGNORE)
3203 if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
3204 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3205 else if(val_is_ground(noisebuf_ground.get(x,y,z),
3206 v3s16(x,y,z), data->seed))
3207 vmanip.m_data[i] = MapNode(CONTENT_STONE);
3208 else if(y <= WATER_LEVEL)
3209 vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
3211 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3214 /*if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
3216 // Only modify places that have no content
3217 if(vmanip.m_data[i].d == CONTENT_IGNORE)
3218 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3220 else if(is_ground(noisebuf_ground.get(x,y,z), y))
3222 // Only modify places that have no content
3223 if(vmanip.m_data[i].d == CONTENT_IGNORE)
3224 vmanip.m_data[i] = MapNode(CONTENT_STONE);
3226 else if(y <= WATER_LEVEL)
3228 // Only modify places that have air or no content
3229 if(vmanip.m_data[i].d == CONTENT_IGNORE
3230 || vmanip.m_data[i].d == CONTENT_AIR)
3231 vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
3235 // Only modify places that have no content
3236 if(vmanip.m_data[i].d == CONTENT_IGNORE)
3237 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3240 data->vmanip.m_area.add_y(em, i, 1);
3253 for(s16 i=0; i<approx_ground_depth/4; i++)
3255 if(myrand()%50 == 0)
3257 s16 x = myrand_range(node_min.X+1, node_max.X-1);
3258 s16 y = myrand_range(node_min.Y+1, node_max.Y-1);
3259 s16 z = myrand_range(node_min.Z+1, node_max.Z-1);
3260 for(u16 i=0; i<27; i++)
3262 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3263 u32 vi = vmanip.m_area.index(p);
3264 if(vmanip.m_data[vi].d == CONTENT_STONE)
3266 vmanip.m_data[vi] = MapNode(CONTENT_MESE);
3275 u16 a = myrand_range(0,15);
3277 u16 amount = 20 * a/1000;
3278 for(s16 i=0; i<amount; i++)
3280 s16 x = myrand_range(node_min.X+1, node_max.X-1);
3281 s16 y = myrand_range(node_min.Y+1, node_max.Y-1);
3282 s16 z = myrand_range(node_min.Z+1, node_max.Z-1);
3284 u8 base_content = CONTENT_STONE;
3285 MapNode new_content(CONTENT_IGNORE);
3288 if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1)
3290 new_content = MapNode(CONTENT_STONE, MINERAL_COAL);
3294 if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0)
3295 new_content = MapNode(CONTENT_STONE, MINERAL_IRON);
3296 /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
3297 vmanip.m_data[i] = MapNode(CONTENT_MUD);
3299 vmanip.m_data[i] = MapNode(CONTENT_SAND);*/
3301 /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1)
3305 if(new_content.d != CONTENT_IGNORE)
3307 for(u16 i=0; i<27; i++)
3309 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3310 u32 vi = vmanip.m_area.index(p);
3311 if(vmanip.m_data[vi].d == base_content)
3313 if(myrand()%sparseness == 0)
3314 vmanip.m_data[vi] = new_content;
3323 //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++)
3324 //for(s16 i=0; i<50; i++)
3325 u16 coal_amount = 30;
3326 u16 coal_rareness = 60 / coal_amount;
3327 if(coal_rareness == 0)
3329 if(myrand()%coal_rareness == 0)
3331 u16 a = myrand() % 16;
3332 u16 amount = coal_amount * a*a*a / 1000;
3333 for(s16 i=0; i<amount; i++)
3335 s16 x = myrand_range(node_min.X+1, node_max.X-1);
3336 s16 y = myrand_range(node_min.Y+1, node_max.Y-1);
3337 s16 z = myrand_range(node_min.Z+1, node_max.Z-1);
3338 for(u16 i=0; i<27; i++)
3340 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3341 u32 vi = vmanip.m_area.index(p);
3342 if(vmanip.m_data[vi].d == CONTENT_STONE)
3344 vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_COAL);
3351 u16 iron_amount = 8;
3352 u16 iron_rareness = 60 / iron_amount;
3353 if(iron_rareness == 0)
3355 if(myrand()%iron_rareness == 0)
3357 u16 a = myrand() % 16;
3358 u16 amount = iron_amount * a*a*a / 1000;
3359 for(s16 i=0; i<amount; i++)
3361 s16 x = myrand_range(node_min.X+1, node_max.X-1);
3362 s16 y = myrand_range(node_min.Y+1, node_max.Y-1);
3363 s16 z = myrand_range(node_min.Z+1, node_max.Z-1);
3364 for(u16 i=0; i<27; i++)
3366 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3367 u32 vi = vmanip.m_area.index(p);
3368 if(vmanip.m_data[vi].d == CONTENT_STONE)
3370 vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_IRON);
3377 Add mud and sand and others underground (in place of stone)
3380 for(s16 x=node_min.X; x<=node_max.X; x++)
3381 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3386 // Use fast index incrementing
3387 v3s16 em = vmanip.m_area.getExtent();
3388 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
3389 for(s16 y=node_max.Y; y>=node_min.Y; y--)
3391 if(vmanip.m_data[i].d == CONTENT_STONE)
3393 if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
3395 if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
3396 vmanip.m_data[i] = MapNode(CONTENT_MUD);
3398 vmanip.m_data[i] = MapNode(CONTENT_SAND);
3400 else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7)
3402 if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
3403 vmanip.m_data[i] = MapNode(CONTENT_GRAVEL);
3407 data->vmanip.m_area.add_y(em, i, -1);
3416 //if(node_min.Y < approx_groundlevel)
3417 //if(myrand() % 3 == 0)
3418 //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
3419 if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel)
3421 // Dungeon generator doesn't modify places which have this set
3422 data->vmanip.clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
3423 | VMANIP_FLAG_DUNGEON_PRESERVE);
3425 // Set all air and water to be untouchable to make dungeons open
3426 // to caves and open air
3427 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
3428 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
3433 // Use fast index incrementing
3434 v3s16 em = vmanip.m_area.getExtent();
3435 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
3436 for(s16 y=node_max.Y; y>=full_node_min.Y; y--)
3438 if(vmanip.m_data[i].d == CONTENT_AIR)
3439 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
3440 else if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
3441 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
3442 data->vmanip.m_area.add_y(em, i, -1);
3447 /*s16 x = myrand_range(node_min.X, node_max.X);
3448 s16 z = myrand_range(node_min.Z, node_max.Z);
3449 s16 y = myrand_range(node_min.Y, node_max.Y);*/
3451 make_dungeon1(data->vmanip);
3453 // Take different seed for every dungeon for not blending their
3454 // mossyness together
3455 //u32 mossyseed = z*38134234+y*42123+x*23;
3456 u32 mossyseed = full_node_min.Z*38134234
3457 +full_node_min.Y*42123+full_node_min.X*23;
3459 // Convert some cobble to mossy cobble
3460 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
3461 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
3466 // Use fast index incrementing
3467 v3s16 em = vmanip.m_area.getExtent();
3468 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
3469 for(s16 y=node_max.Y; y>=full_node_min.Y; y--)
3471 // No mossy in dry places
3472 // (noisebuf not used because it's smaller in size)
3473 if(noise3d_param(get_ground_wetness_params(data->seed), x,y,z)
3476 double d = noise3d_perlin((float)x/4.,
3477 (float)y/4.,(float)z/4.,
3481 if(vmanip.m_data[i].d == CONTENT_COBBLE)
3482 vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE;
3483 data->vmanip.m_area.add_y(em, i, -1);
3490 Add top and bottom side of water to transforming_liquid queue
3493 for(s16 x=node_min.X; x<=node_max.X; x++)
3494 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3499 bool water_found = false;
3500 // Use fast index incrementing
3501 v3s16 em = vmanip.m_area.getExtent();
3502 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
3503 for(s16 y=node_max.Y; y>=node_min.Y; y--)
3505 if(water_found == false)
3507 if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
3509 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3510 data->transforming_liquid.push_back(p);
3516 // This can be done because water_found can only
3517 // turn to true and end up here after going through
3519 if(vmanip.m_data[i+1].d != CONTENT_WATERSOURCE)
3521 v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
3522 data->transforming_liquid.push_back(p);
3523 water_found = false;
3527 data->vmanip.m_area.add_y(em, i, -1);
3533 If close to ground level
3536 if(abs(approx_ground_depth) < 20)
3542 for(s16 x=node_min.X; x<=node_max.X; x++)
3543 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3548 bool possibly_have_sand = get_have_sand(data->seed, p2d);
3549 bool have_sand = false;
3551 bool air_detected = false;
3552 bool water_detected = false;
3553 // Use fast index incrementing
3554 s16 start_y = node_max.Y+2;
3555 v3s16 em = vmanip.m_area.getExtent();
3556 u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
3557 for(s16 y=start_y; y>=node_min.Y-2; y--)
3559 if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
3560 water_detected = true;
3561 if(vmanip.m_data[i].d == CONTENT_AIR)
3562 air_detected = true;
3564 if((vmanip.m_data[i].d == CONTENT_STONE
3565 || vmanip.m_data[i].d == CONTENT_GRASS
3566 || vmanip.m_data[i].d == CONTENT_MUD
3567 || vmanip.m_data[i].d == CONTENT_SAND
3568 || vmanip.m_data[i].d == CONTENT_GRAVEL
3569 ) && (air_detected || water_detected))
3571 if(mud_count == 0 && y <= WATER_LEVEL+2
3572 && possibly_have_sand)
3577 vmanip.m_data[i] = MapNode(CONTENT_SAND);
3579 else if(mud_count==0 && !water_detected && y >= WATER_LEVEL)
3580 vmanip.m_data[i] = MapNode(CONTENT_GRASS);
3582 vmanip.m_data[i] = MapNode(CONTENT_MUD);
3588 else if(mud_count != 0)
3591 data->vmanip.m_area.add_y(em, i, -1);
3601 u32 tree_count = block_area_nodes * tree_amount_2d(data->seed, p2d_center);
3602 // Put trees in random places on part of division
3603 for(u32 i=0; i<tree_count; i++)
3605 s16 x = myrand_range(node_min.X, node_max.X);
3606 s16 z = myrand_range(node_min.Z, node_max.Z);
3607 //s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3608 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
3609 // Don't make a tree under water level
3612 // Make sure tree fits (only trees whose starting point is
3613 // at this block are added)
3614 if(y < node_min.Y || y > node_max.Y)
3617 Find exact ground level
3621 for(; p.Y >= y-6; p.Y--)
3623 u32 i = data->vmanip.m_area.index(p);
3624 MapNode *n = &data->vmanip.m_data[i];
3625 if(n->d != CONTENT_AIR && n->d != CONTENT_IGNORE)
3631 // If not found, handle next one
3635 Trees grow only on mud and grass
3638 u32 i = data->vmanip.m_area.index(p);
3639 MapNode *n = &data->vmanip.m_data[i];
3640 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3643 // Tree will be placed one higher
3646 make_tree(data->vmanip, p);
3651 Add some kind of random stones
3654 u32 random_stone_count = block_area_nodes *
3655 randomstone_amount_2d(data->seed, p2d_center);
3656 // Put in random places on part of division
3657 for(u32 i=0; i<random_stone_count; i++)
3659 s16 x = myrand_range(node_min.X, node_max.X);
3660 s16 z = myrand_range(node_min.Z, node_max.Z);
3661 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
3662 // Don't add under water level
3663 /*if(y < WATER_LEVEL)
3665 // Don't add if doesn't belong to this block
3666 if(y < node_min.Y || y > node_max.Y)
3671 u32 i = data->vmanip.m_area.index(v3s16(p));
3672 MapNode *n = &data->vmanip.m_data[i];
3673 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3676 // Will be placed one higher
3679 make_randomstone(data->vmanip, p);
3688 u32 large_stone_count = block_area_nodes *
3689 largestone_amount_2d(data->seed, p2d_center);
3690 //u32 large_stone_count = 1;
3691 // Put in random places on part of division
3692 for(u32 i=0; i<large_stone_count; i++)
3694 s16 x = myrand_range(node_min.X, node_max.X);
3695 s16 z = myrand_range(node_min.Z, node_max.Z);
3696 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
3697 // Don't add under water level
3698 /*if(y < WATER_LEVEL)
3700 // Don't add if doesn't belong to this block
3701 if(y < node_min.Y || y > node_max.Y)
3706 u32 i = data->vmanip.m_area.index(v3s16(p));
3707 MapNode *n = &data->vmanip.m_data[i];
3708 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3711 // Will be placed one lower
3714 make_largestone(data->vmanip, p);
3721 void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
3723 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
3724 <<blockpos.Z<<")"<<std::endl;*/
3726 data->no_op = false;
3727 data->seed = m_seed;
3728 data->blockpos = blockpos;
3731 Create the whole area of this and the neighboring blocks
3734 //TimeTaker timer("initBlockMake() create area");
3736 for(s16 x=-1; x<=1; x++)
3737 for(s16 z=-1; z<=1; z++)
3739 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
3740 // Sector metadata is loaded from disk if not already loaded.
3741 ServerMapSector *sector = createSector(sectorpos);
3744 for(s16 y=-1; y<=1; y++)
3746 MapBlock *block = createBlock(blockpos);
3748 // Lighting won't be calculated
3749 block->setLightingExpired(true);
3750 // Lighting will be calculated
3751 //block->setLightingExpired(false);
3754 Block gets sunlight if this is true.
3756 This should be set to true when the top side of a block
3757 is completely exposed to the sky.
3759 block->setIsUnderground(false);
3765 Now we have a big empty area.
3767 Make a ManualMapVoxelManipulator that contains this and the
3771 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
3772 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
3774 data->vmanip.setMap(this);
3778 //TimeTaker timer("initBlockMake() initialEmerge");
3779 data->vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3782 // Data is ready now.
3785 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
3786 core::map<v3s16, MapBlock*> &changed_blocks)
3788 v3s16 blockpos = data->blockpos;
3789 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
3790 <<blockpos.Z<<")"<<std::endl;*/
3794 dstream<<"finishBlockMake(): no-op"<<std::endl;
3798 /*dstream<<"Resulting vmanip:"<<std::endl;
3799 data->vmanip.print(dstream);*/
3802 Blit generated stuff to map
3806 //TimeTaker timer("finishBlockMake() blitBackAll");
3807 data->vmanip.blitBackAll(&changed_blocks);
3810 //dstream<<"changed_blocks.size()="<<changed_blocks.size()<<std::endl;
3813 Copy transforming liquid information
3815 while(data->transforming_liquid.size() > 0)
3817 v3s16 p = data->transforming_liquid.pop_front();
3818 m_transforming_liquid.push_back(p);
3824 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
3828 Set is_underground flag for lighting with sunlight
3831 s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
3832 data->seed, v2s16(blockpos.X, blockpos.Z));
3834 if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
3835 block->setIsUnderground(true);
3837 block->setIsUnderground(false);
3840 Add sunlight to central block.
3841 This makes in-dark-spawning monsters to not flood the whole thing.
3842 Do not spread the light, though.
3844 /*core::map<v3s16, bool> light_sources;
3845 bool black_air_left = false;
3846 block->propagateSunlight(light_sources, true, &black_air_left);*/
3849 NOTE: Lighting and object adding shouldn't really be here, but
3850 lighting is a bit tricky to move properly to makeBlock.
3851 TODO: Do this the right way anyway.
3857 core::map<v3s16, MapBlock*> lighting_update_blocks;
3858 lighting_update_blocks.insert(block->getPos(), block);
3859 /*for(core::map<v3s16, MapBlock*>::Iterator
3860 i = modified_blocks.getIterator();
3861 i.atEnd() == false; i++)
3863 lighting_update_blocks.insert(i.getNode()->getKey(),
3864 i.getNode()->getValue());
3866 updateLighting(lighting_update_blocks, changed_blocks);
3869 Add random objects to block
3871 addRandomObjects(block);
3874 Go through changed blocks
3876 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3877 i.atEnd() == false; i++)
3879 MapBlock *block = i.getNode()->getValue();
3882 Update day/night difference cache of the MapBlocks
3884 block->updateDayNightDiff();
3886 Set block as modified
3888 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3892 Set central block as generated
3894 block->setGenerated(true);
3897 Save changed parts of map
3898 NOTE: Will be saved later.
3902 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
3903 <<blockpos.Z<<")"<<std::endl;*/
3908 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3910 DSTACKF("%s: p2d=(%d,%d)",
3915 Check if it exists already in memory
3917 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3922 Try to load it from disk (with blocks)
3924 //if(loadSectorFull(p2d) == true)
3927 Try to load metadata from disk
3929 if(loadSectorMeta(p2d) == true)
3931 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3934 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3935 throw InvalidPositionException("");
3941 Do not create over-limit
3943 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3944 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3945 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3946 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3947 throw InvalidPositionException("createSector(): pos. over limit");
3950 Generate blank sector
3953 sector = new ServerMapSector(this, p2d);
3955 // Sector position on map in nodes
3956 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3961 m_sectors.insert(p2d, sector);
3967 This is a quick-hand function for calling makeBlock().
3969 MapBlock * ServerMap::generateBlock(
3971 core::map<v3s16, MapBlock*> &modified_blocks
3974 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
3976 /*dstream<<"generateBlock(): "
3977 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3980 //MapBlock *block = original_dummy;
3982 v2s16 p2d(p.X, p.Z);
3983 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3986 Do not generate over-limit
3988 if(blockpos_over_limit(p))
3990 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3991 throw InvalidPositionException("generateBlock(): pos. over limit");
3995 Create block make data
3998 initBlockMake(&data, p);
4004 TimeTaker t("makeBlock()");
4009 Blit data back on map, update lighting, add mobs and whatever this does
4011 finishBlockMake(&data, modified_blocks);
4016 MapBlock *block = getBlockNoCreateNoEx(p);
4023 bool erroneus_content = false;
4024 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4025 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4026 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4029 MapNode n = block->getNode(p);
4030 if(n.d == CONTENT_IGNORE)
4032 dstream<<"CONTENT_IGNORE at "
4033 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4035 erroneus_content = true;
4039 if(erroneus_content)
4047 Generate a completely empty block
4049 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4050 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4052 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4058 n.d = CONTENT_STONE;
4059 block->setNode(v3s16(x0,y0,z0), n);
4067 MapBlock * ServerMap::createBlock(v3s16 p)
4069 DSTACKF("%s: p=(%d,%d,%d)",
4070 __FUNCTION_NAME, p.X, p.Y, p.Z);
4073 Do not create over-limit
4075 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4076 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4077 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4078 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4079 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4080 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4081 throw InvalidPositionException("createBlock(): pos. over limit");
4083 v2s16 p2d(p.X, p.Z);
4086 This will create or load a sector if not found in memory.
4087 If block exists on disk, it will be loaded.
4089 NOTE: On old save formats, this will be slow, as it generates
4090 lighting on blocks for them.
4092 ServerMapSector *sector;
4094 sector = (ServerMapSector*)createSector(p2d);
4095 assert(sector->getId() == MAPSECTOR_SERVER);
4097 catch(InvalidPositionException &e)
4099 dstream<<"createBlock: createSector() failed"<<std::endl;
4103 NOTE: This should not be done, or at least the exception
4104 should not be passed on as std::exception, because it
4105 won't be catched at all.
4107 /*catch(std::exception &e)
4109 dstream<<"createBlock: createSector() failed: "
4110 <<e.what()<<std::endl;
4115 Try to get a block from the sector
4118 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4121 if(block->isDummy())
4126 block = sector->createBlankBlock(block_y);
4131 MapBlock * ServerMap::emergeBlock(
4133 bool only_from_disk,
4134 core::map<v3s16, MapBlock*> &changed_blocks,
4135 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4138 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4140 p.X, p.Y, p.Z, only_from_disk);
4142 // This has to be redone or removed
4150 Do not generate over-limit
4152 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4153 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4154 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4155 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4156 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4157 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4158 throw InvalidPositionException("emergeBlock(): pos. over limit");
4160 v2s16 p2d(p.X, p.Z);
4163 This will create or load a sector if not found in memory.
4164 If block exists on disk, it will be loaded.
4166 ServerMapSector *sector;
4168 sector = createSector(p2d);
4169 //sector = emergeSector(p2d, changed_blocks);
4171 catch(InvalidPositionException &e)
4173 dstream<<"emergeBlock: createSector() failed: "
4174 <<e.what()<<std::endl;
4175 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4177 <<"You could try to delete it."<<std::endl;
4180 catch(VersionMismatchException &e)
4182 dstream<<"emergeBlock: createSector() failed: "
4183 <<e.what()<<std::endl;
4184 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4186 <<"You could try to delete it."<<std::endl;
4191 Try to get a block from the sector
4194 bool does_not_exist = false;
4195 bool lighting_expired = false;
4196 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4198 // If not found, try loading from disk
4201 block = loadBlock(p);
4207 does_not_exist = true;
4209 else if(block->isDummy() == true)
4211 does_not_exist = true;
4213 else if(block->getLightingExpired())
4215 lighting_expired = true;
4220 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4225 If block was not found on disk and not going to generate a
4226 new one, make sure there is a dummy block in place.
4228 if(only_from_disk && (does_not_exist || lighting_expired))
4230 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4234 // Create dummy block
4235 block = new MapBlock(this, p, true);
4237 // Add block to sector
4238 sector->insertBlock(block);
4244 //dstream<<"Not found on disk, generating."<<std::endl;
4246 //TimeTaker("emergeBlock() generate");
4248 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4251 If the block doesn't exist, generate the block.
4255 block = generateBlock(p, block, sector, changed_blocks,
4256 lighting_invalidated_blocks);
4259 if(lighting_expired)
4261 lighting_invalidated_blocks.insert(p, block);
4266 Initially update sunlight
4269 core::map<v3s16, bool> light_sources;
4270 bool black_air_left = false;
4271 bool bottom_invalid =
4272 block->propagateSunlight(light_sources, true,
4275 // If sunlight didn't reach everywhere and part of block is
4276 // above ground, lighting has to be properly updated
4277 //if(black_air_left && some_part_underground)
4280 lighting_invalidated_blocks[block->getPos()] = block;
4285 lighting_invalidated_blocks[block->getPos()] = block;
4294 s16 ServerMap::findGroundLevel(v2s16 p2d)
4298 Uh, just do something random...
4300 // Find existing map from top to down
4303 v3s16 p(p2d.X, max, p2d.Y);
4304 for(; p.Y>min; p.Y--)
4306 MapNode n = getNodeNoEx(p);
4307 if(n.d != CONTENT_IGNORE)
4312 // If this node is not air, go to plan b
4313 if(getNodeNoEx(p).d != CONTENT_AIR)
4315 // Search existing walkable and return it
4316 for(; p.Y>min; p.Y--)
4318 MapNode n = getNodeNoEx(p);
4319 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4328 Determine from map generator noise functions
4331 s16 level = find_ground_level_from_noise(m_seed, p2d, 1);
4334 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4335 //return (s16)level;
4338 void ServerMap::createDirs(std::string path)
4340 if(fs::CreateAllDirs(path) == false)
4342 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4343 <<"\""<<path<<"\""<<std::endl;
4344 throw BaseException("ServerMap failed to create directory");
4348 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4354 snprintf(cc, 9, "%.4x%.4x",
4355 (unsigned int)pos.X&0xffff,
4356 (unsigned int)pos.Y&0xffff);
4358 return m_savedir + "/sectors/" + cc;
4360 snprintf(cc, 9, "%.3x/%.3x",
4361 (unsigned int)pos.X&0xfff,
4362 (unsigned int)pos.Y&0xfff);
4364 return m_savedir + "/sectors2/" + cc;
4370 v2s16 ServerMap::getSectorPos(std::string dirname)
4374 size_t spos = dirname.rfind('/') + 1;
4375 assert(spos != std::string::npos);
4376 if(dirname.size() - spos == 8)
4379 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4381 else if(dirname.size() - spos == 3)
4384 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4385 // Sign-extend the 12 bit values up to 16 bits...
4386 if(x&0x800) x|=0xF000;
4387 if(y&0x800) y|=0xF000;
4394 v2s16 pos((s16)x, (s16)y);
4398 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4400 v2s16 p2d = getSectorPos(sectordir);
4402 if(blockfile.size() != 4){
4403 throw InvalidFilenameException("Invalid block filename");
4406 int r = sscanf(blockfile.c_str(), "%4x", &y);
4408 throw InvalidFilenameException("Invalid block filename");
4409 return v3s16(p2d.X, y, p2d.Y);
4412 std::string ServerMap::getBlockFilename(v3s16 p)
4415 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
4419 void ServerMap::save(bool only_changed)
4421 DSTACK(__FUNCTION_NAME);
4422 if(m_map_saving_enabled == false)
4424 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4428 if(only_changed == false)
4429 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4432 if(only_changed == false || m_map_metadata_changed)
4437 u32 sector_meta_count = 0;
4438 u32 block_count = 0;
4439 u32 block_count_all = 0; // Number of blocks in memory
4441 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4442 for(; i.atEnd() == false; i++)
4444 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4445 assert(sector->getId() == MAPSECTOR_SERVER);
4447 if(sector->differs_from_disk || only_changed == false)
4449 saveSectorMeta(sector);
4450 sector_meta_count++;
4452 core::list<MapBlock*> blocks;
4453 sector->getBlocks(blocks);
4454 core::list<MapBlock*>::Iterator j;
4455 for(j=blocks.begin(); j!=blocks.end(); j++)
4457 MapBlock *block = *j;
4461 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
4462 || only_changed == false)
4467 /*dstream<<"ServerMap: Written block ("
4468 <<block->getPos().X<<","
4469 <<block->getPos().Y<<","
4470 <<block->getPos().Z<<")"
4477 Only print if something happened or saved whole map
4479 if(only_changed == false || sector_meta_count != 0
4480 || block_count != 0)
4482 dstream<<DTIME<<"ServerMap: Written: "
4483 <<sector_meta_count<<" sector metadata files, "
4484 <<block_count<<" block files"
4485 <<", "<<block_count_all<<" blocks in memory."
4490 void ServerMap::saveMapMeta()
4492 DSTACK(__FUNCTION_NAME);
4494 dstream<<"INFO: ServerMap::saveMapMeta(): "
4498 createDirs(m_savedir);
4500 std::string fullpath = m_savedir + "/map_meta.txt";
4501 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4502 if(os.good() == false)
4504 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4505 <<"could not open"<<fullpath<<std::endl;
4506 throw FileNotGoodException("Cannot open chunk metadata");
4510 params.setU64("seed", m_seed);
4512 params.writeLines(os);
4514 os<<"[end_of_params]\n";
4516 m_map_metadata_changed = false;
4519 void ServerMap::loadMapMeta()
4521 DSTACK(__FUNCTION_NAME);
4523 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
4526 std::string fullpath = m_savedir + "/map_meta.txt";
4527 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4528 if(is.good() == false)
4530 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4531 <<"could not open"<<fullpath<<std::endl;
4532 throw FileNotGoodException("Cannot open map metadata");
4540 throw SerializationError
4541 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4543 std::getline(is, line);
4544 std::string trimmedline = trim(line);
4545 if(trimmedline == "[end_of_params]")
4547 params.parseConfigLine(line);
4550 m_seed = params.getU64("seed");
4552 dstream<<"INFO: ServerMap::loadMapMeta(): "
4557 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4559 DSTACK(__FUNCTION_NAME);
4560 // Format used for writing
4561 u8 version = SER_FMT_VER_HIGHEST;
4563 v2s16 pos = sector->getPos();
4564 std::string dir = getSectorDir(pos);
4567 std::string fullpath = dir + "/meta";
4568 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4569 if(o.good() == false)
4570 throw FileNotGoodException("Cannot open sector metafile");
4572 sector->serialize(o, version);
4574 sector->differs_from_disk = false;
4577 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
4579 DSTACK(__FUNCTION_NAME);
4581 v2s16 p2d = getSectorPos(sectordir);
4583 ServerMapSector *sector = NULL;
4585 std::string fullpath = sectordir + "/meta";
4586 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4587 if(is.good() == false)
4589 // If the directory exists anyway, it probably is in some old
4590 // format. Just go ahead and create the sector.
4591 if(fs::PathExists(sectordir))
4593 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
4594 <<fullpath<<" doesn't exist but directory does."
4595 <<" Continuing with a sector with no metadata."
4597 sector = new ServerMapSector(this, p2d);
4598 m_sectors.insert(p2d, sector);
4602 throw FileNotGoodException("Cannot open sector metafile");
4607 sector = ServerMapSector::deSerialize
4608 (is, this, p2d, m_sectors);
4610 saveSectorMeta(sector);
4613 sector->differs_from_disk = false;
4618 bool ServerMap::loadSectorMeta(v2s16 p2d)
4620 DSTACK(__FUNCTION_NAME);
4622 MapSector *sector = NULL;
4624 // The directory layout we're going to load from.
4625 // 1 - original sectors/xxxxzzzz/
4626 // 2 - new sectors2/xxx/zzz/
4627 // If we load from anything but the latest structure, we will
4628 // immediately save to the new one, and remove the old.
4630 std::string sectordir1 = getSectorDir(p2d, 1);
4631 std::string sectordir;
4632 if(fs::PathExists(sectordir1))
4634 sectordir = sectordir1;
4639 sectordir = getSectorDir(p2d, 2);
4643 sector = loadSectorMeta(sectordir, loadlayout != 2);
4645 catch(InvalidFilenameException &e)
4649 catch(FileNotGoodException &e)
4653 catch(std::exception &e)
4662 bool ServerMap::loadSectorFull(v2s16 p2d)
4664 DSTACK(__FUNCTION_NAME);
4666 MapSector *sector = NULL;
4668 // The directory layout we're going to load from.
4669 // 1 - original sectors/xxxxzzzz/
4670 // 2 - new sectors2/xxx/zzz/
4671 // If we load from anything but the latest structure, we will
4672 // immediately save to the new one, and remove the old.
4674 std::string sectordir1 = getSectorDir(p2d, 1);
4675 std::string sectordir;
4676 if(fs::PathExists(sectordir1))
4678 sectordir = sectordir1;
4683 sectordir = getSectorDir(p2d, 2);
4687 sector = loadSectorMeta(sectordir, loadlayout != 2);
4689 catch(InvalidFilenameException &e)
4693 catch(FileNotGoodException &e)
4697 catch(std::exception &e)
4705 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4707 std::vector<fs::DirListNode>::iterator i2;
4708 for(i2=list2.begin(); i2!=list2.end(); i2++)
4714 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
4716 catch(InvalidFilenameException &e)
4718 // This catches unknown crap in directory
4724 dstream<<"Sector converted to new layout - deleting "<<
4725 sectordir1<<std::endl;
4726 fs::RecursiveDelete(sectordir1);
4733 void ServerMap::saveBlock(MapBlock *block)
4735 DSTACK(__FUNCTION_NAME);
4737 Dummy blocks are not written
4739 if(block->isDummy())
4741 /*v3s16 p = block->getPos();
4742 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4743 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4747 // Format used for writing
4748 u8 version = SER_FMT_VER_HIGHEST;
4750 v3s16 p3d = block->getPos();
4752 v2s16 p2d(p3d.X, p3d.Z);
4753 std::string sectordir = getSectorDir(p2d);
4755 createDirs(sectordir);
4757 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
4758 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4759 if(o.good() == false)
4760 throw FileNotGoodException("Cannot open block data");
4763 [0] u8 serialization version
4766 o.write((char*)&version, 1);
4769 block->serialize(o, version);
4771 // Write extra data stored on disk
4772 block->serializeDiskExtra(o, version);
4774 // We just wrote it to the disk so clear modified flag
4775 block->resetModified();
4778 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
4780 DSTACK(__FUNCTION_NAME);
4782 std::string fullpath = sectordir+"/"+blockfile;
4785 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4786 if(is.good() == false)
4787 throw FileNotGoodException("Cannot open block file");
4789 v3s16 p3d = getBlockPos(sectordir, blockfile);
4790 v2s16 p2d(p3d.X, p3d.Z);
4792 assert(sector->getPos() == p2d);
4794 u8 version = SER_FMT_VER_INVALID;
4795 is.read((char*)&version, 1);
4798 throw SerializationError("ServerMap::loadBlock(): Failed"
4799 " to read MapBlock version");
4801 /*u32 block_size = MapBlock::serializedLength(version);
4802 SharedBuffer<u8> data(block_size);
4803 is.read((char*)*data, block_size);*/
4805 // This will always return a sector because we're the server
4806 //MapSector *sector = emergeSector(p2d);
4808 MapBlock *block = NULL;
4809 bool created_new = false;
4811 block = sector->getBlockNoCreate(p3d.Y);
4813 catch(InvalidPositionException &e)
4815 block = sector->createBlankBlockNoInsert(p3d.Y);
4820 block->deSerialize(is, version);
4822 // Read extra data stored on disk
4823 block->deSerializeDiskExtra(is, version);
4825 // If it's a new block, insert it to the map
4827 sector->insertBlock(block);
4830 Save blocks loaded in old format in new format
4833 if(version < SER_FMT_VER_HIGHEST || save_after_load)
4838 // We just loaded it from the disk, so it's up-to-date.
4839 block->resetModified();
4842 catch(SerializationError &e)
4844 dstream<<"WARNING: Invalid block data on disk "
4845 <<"fullpath="<<fullpath
4846 <<" (SerializationError). "
4847 <<"what()="<<e.what()
4849 //" Ignoring. A new one will be generated.
4852 // TODO: Backup file; name is in fullpath.
4856 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
4858 DSTACK(__FUNCTION_NAME);
4860 v2s16 p2d(blockpos.X, blockpos.Z);
4862 // The directory layout we're going to load from.
4863 // 1 - original sectors/xxxxzzzz/
4864 // 2 - new sectors2/xxx/zzz/
4865 // If we load from anything but the latest structure, we will
4866 // immediately save to the new one, and remove the old.
4868 std::string sectordir1 = getSectorDir(p2d, 1);
4869 std::string sectordir;
4870 if(fs::PathExists(sectordir1))
4872 sectordir = sectordir1;
4877 sectordir = getSectorDir(p2d, 2);
4881 Make sure sector is loaded
4883 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4887 sector = loadSectorMeta(sectordir, loadlayout != 2);
4889 catch(InvalidFilenameException &e)
4893 catch(FileNotGoodException &e)
4897 catch(std::exception &e)
4904 Make sure file exists
4907 std::string blockfilename = getBlockFilename(blockpos);
4908 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
4914 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
4915 return getBlockNoCreateNoEx(blockpos);
4918 void ServerMap::PrintInfo(std::ostream &out)
4929 ClientMap::ClientMap(
4931 MapDrawControl &control,
4932 scene::ISceneNode* parent,
4933 scene::ISceneManager* mgr,
4937 scene::ISceneNode(parent, mgr, id),
4940 m_camera_position(0,0,0),
4941 m_camera_direction(0,0,1)
4943 m_camera_mutex.Init();
4944 assert(m_camera_mutex.IsInitialized());
4946 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4947 BS*1000000,BS*1000000,BS*1000000);
4950 ClientMap::~ClientMap()
4952 /*JMutexAutoLock lock(mesh_mutex);
4961 MapSector * ClientMap::emergeSector(v2s16 p2d)
4963 DSTACK(__FUNCTION_NAME);
4964 // Check that it doesn't exist already
4966 return getSectorNoGenerate(p2d);
4968 catch(InvalidPositionException &e)
4973 ClientMapSector *sector = new ClientMapSector(this, p2d);
4976 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4977 m_sectors.insert(p2d, sector);
4983 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
4985 DSTACK(__FUNCTION_NAME);
4986 ClientMapSector *sector = NULL;
4988 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4990 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
4994 sector = (ClientMapSector*)n->getValue();
4995 assert(sector->getId() == MAPSECTOR_CLIENT);
4999 sector = new ClientMapSector(this, p2d);
5001 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5002 m_sectors.insert(p2d, sector);
5006 sector->deSerialize(is);
5009 void ClientMap::OnRegisterSceneNode()
5013 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5014 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5017 ISceneNode::OnRegisterSceneNode();
5020 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5022 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5023 DSTACK(__FUNCTION_NAME);
5025 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5028 This is called two times per frame, reset on the non-transparent one
5030 if(pass == scene::ESNRP_SOLID)
5032 m_last_drawn_sectors.clear();
5036 Get time for measuring timeout.
5038 Measuring time is very useful for long delays when the
5039 machine is swapping a lot.
5041 int time1 = time(0);
5043 //u32 daynight_ratio = m_client->getDayNightRatio();
5045 m_camera_mutex.Lock();
5046 v3f camera_position = m_camera_position;
5047 v3f camera_direction = m_camera_direction;
5048 m_camera_mutex.Unlock();
5051 Get all blocks and draw all visible ones
5054 v3s16 cam_pos_nodes(
5055 camera_position.X / BS,
5056 camera_position.Y / BS,
5057 camera_position.Z / BS);
5059 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5061 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5062 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5064 // Take a fair amount as we will be dropping more out later
5066 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5067 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5068 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5070 p_nodes_max.X / MAP_BLOCKSIZE,
5071 p_nodes_max.Y / MAP_BLOCKSIZE,
5072 p_nodes_max.Z / MAP_BLOCKSIZE);
5074 u32 vertex_count = 0;
5076 // For limiting number of mesh updates per frame
5077 u32 mesh_update_count = 0;
5079 u32 blocks_would_have_drawn = 0;
5080 u32 blocks_drawn = 0;
5082 int timecheck_counter = 0;
5083 core::map<v2s16, MapSector*>::Iterator si;
5084 si = m_sectors.getIterator();
5085 for(; si.atEnd() == false; si++)
5088 timecheck_counter++;
5089 if(timecheck_counter > 50)
5091 timecheck_counter = 0;
5092 int time2 = time(0);
5093 if(time2 > time1 + 4)
5095 dstream<<"ClientMap::renderMap(): "
5096 "Rendering takes ages, returning."
5103 MapSector *sector = si.getNode()->getValue();
5104 v2s16 sp = sector->getPos();
5106 if(m_control.range_all == false)
5108 if(sp.X < p_blocks_min.X
5109 || sp.X > p_blocks_max.X
5110 || sp.Y < p_blocks_min.Z
5111 || sp.Y > p_blocks_max.Z)
5115 core::list< MapBlock * > sectorblocks;
5116 sector->getBlocks(sectorblocks);
5122 u32 sector_blocks_drawn = 0;
5124 core::list< MapBlock * >::Iterator i;
5125 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5127 MapBlock *block = *i;
5130 Compare block position to camera position, skip
5131 if not seen on display
5134 float range = 100000 * BS;
5135 if(m_control.range_all == false)
5136 range = m_control.wanted_range * BS;
5139 if(isBlockInSight(block->getPos(), camera_position,
5140 camera_direction, range, &d) == false)
5145 // This is ugly (spherical distance limit?)
5146 /*if(m_control.range_all == false &&
5147 d - 0.5*BS*MAP_BLOCKSIZE > range)
5152 Update expired mesh (used for day/night change)
5154 It doesn't work exactly like it should now with the
5155 tasked mesh update but whatever.
5158 bool mesh_expired = false;
5161 JMutexAutoLock lock(block->mesh_mutex);
5163 mesh_expired = block->getMeshExpired();
5165 // Mesh has not been expired and there is no mesh:
5166 // block has no content
5167 if(block->mesh == NULL && mesh_expired == false)
5171 f32 faraway = BS*50;
5172 //f32 faraway = m_control.wanted_range * BS;
5175 This has to be done with the mesh_mutex unlocked
5177 // Pretty random but this should work somewhat nicely
5178 if(mesh_expired && (
5179 (mesh_update_count < 3
5180 && (d < faraway || mesh_update_count < 2)
5183 (m_control.range_all && mesh_update_count < 20)
5186 /*if(mesh_expired && mesh_update_count < 6
5187 && (d < faraway || mesh_update_count < 3))*/
5189 mesh_update_count++;
5191 // Mesh has been expired: generate new mesh
5192 //block->updateMesh(daynight_ratio);
5193 m_client->addUpdateMeshTask(block->getPos());
5195 mesh_expired = false;
5200 Draw the faces of the block
5203 JMutexAutoLock lock(block->mesh_mutex);
5205 scene::SMesh *mesh = block->mesh;
5210 blocks_would_have_drawn++;
5211 if(blocks_drawn >= m_control.wanted_max_blocks
5212 && m_control.range_all == false
5213 && d > m_control.wanted_min_range * BS)
5217 sector_blocks_drawn++;
5219 u32 c = mesh->getMeshBufferCount();
5221 for(u32 i=0; i<c; i++)
5223 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5224 const video::SMaterial& material = buf->getMaterial();
5225 video::IMaterialRenderer* rnd =
5226 driver->getMaterialRenderer(material.MaterialType);
5227 bool transparent = (rnd && rnd->isTransparent());
5228 // Render transparent on transparent pass and likewise.
5229 if(transparent == is_transparent_pass)
5232 This *shouldn't* hurt too much because Irrlicht
5233 doesn't change opengl textures if the old
5234 material is set again.
5236 driver->setMaterial(buf->getMaterial());
5237 driver->drawMeshBuffer(buf);
5238 vertex_count += buf->getVertexCount();
5242 } // foreach sectorblocks
5244 if(sector_blocks_drawn != 0)
5246 m_last_drawn_sectors[sp] = true;
5250 m_control.blocks_drawn = blocks_drawn;
5251 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5253 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5254 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5257 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5258 core::map<v3s16, MapBlock*> *affected_blocks)
5260 bool changed = false;
5262 Add it to all blocks touching it
5265 v3s16(0,0,0), // this
5266 v3s16(0,0,1), // back
5267 v3s16(0,1,0), // top
5268 v3s16(1,0,0), // right
5269 v3s16(0,0,-1), // front
5270 v3s16(0,-1,0), // bottom
5271 v3s16(-1,0,0), // left
5273 for(u16 i=0; i<7; i++)
5275 v3s16 p2 = p + dirs[i];
5276 // Block position of neighbor (or requested) node
5277 v3s16 blockpos = getNodeBlockPos(p2);
5278 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5279 if(blockref == NULL)
5281 // Relative position of requested node
5282 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5283 if(blockref->setTempMod(relpos, mod))
5288 if(changed && affected_blocks!=NULL)
5290 for(u16 i=0; i<7; i++)
5292 v3s16 p2 = p + dirs[i];
5293 // Block position of neighbor (or requested) node
5294 v3s16 blockpos = getNodeBlockPos(p2);
5295 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5296 if(blockref == NULL)
5298 affected_blocks->insert(blockpos, blockref);
5304 bool ClientMap::clearTempMod(v3s16 p,
5305 core::map<v3s16, MapBlock*> *affected_blocks)
5307 bool changed = false;
5309 v3s16(0,0,0), // this
5310 v3s16(0,0,1), // back
5311 v3s16(0,1,0), // top
5312 v3s16(1,0,0), // right
5313 v3s16(0,0,-1), // front
5314 v3s16(0,-1,0), // bottom
5315 v3s16(-1,0,0), // left
5317 for(u16 i=0; i<7; i++)
5319 v3s16 p2 = p + dirs[i];
5320 // Block position of neighbor (or requested) node
5321 v3s16 blockpos = getNodeBlockPos(p2);
5322 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5323 if(blockref == NULL)
5325 // Relative position of requested node
5326 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5327 if(blockref->clearTempMod(relpos))
5332 if(changed && affected_blocks!=NULL)
5334 for(u16 i=0; i<7; i++)
5336 v3s16 p2 = p + dirs[i];
5337 // Block position of neighbor (or requested) node
5338 v3s16 blockpos = getNodeBlockPos(p2);
5339 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5340 if(blockref == NULL)
5342 affected_blocks->insert(blockpos, blockref);
5348 void ClientMap::expireMeshes(bool only_daynight_diffed)
5350 TimeTaker timer("expireMeshes()");
5352 core::map<v2s16, MapSector*>::Iterator si;
5353 si = m_sectors.getIterator();
5354 for(; si.atEnd() == false; si++)
5356 MapSector *sector = si.getNode()->getValue();
5358 core::list< MapBlock * > sectorblocks;
5359 sector->getBlocks(sectorblocks);
5361 core::list< MapBlock * >::Iterator i;
5362 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5364 MapBlock *block = *i;
5366 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5372 JMutexAutoLock lock(block->mesh_mutex);
5373 if(block->mesh != NULL)
5375 /*block->mesh->drop();
5376 block->mesh = NULL;*/
5377 block->setMeshExpired(true);
5384 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5386 assert(mapType() == MAPTYPE_CLIENT);
5389 v3s16 p = blockpos + v3s16(0,0,0);
5390 MapBlock *b = getBlockNoCreate(p);
5391 b->updateMesh(daynight_ratio);
5392 //b->setMeshExpired(true);
5394 catch(InvalidPositionException &e){}
5397 v3s16 p = blockpos + v3s16(-1,0,0);
5398 MapBlock *b = getBlockNoCreate(p);
5399 b->updateMesh(daynight_ratio);
5400 //b->setMeshExpired(true);
5402 catch(InvalidPositionException &e){}
5404 v3s16 p = blockpos + v3s16(0,-1,0);
5405 MapBlock *b = getBlockNoCreate(p);
5406 b->updateMesh(daynight_ratio);
5407 //b->setMeshExpired(true);
5409 catch(InvalidPositionException &e){}
5411 v3s16 p = blockpos + v3s16(0,0,-1);
5412 MapBlock *b = getBlockNoCreate(p);
5413 b->updateMesh(daynight_ratio);
5414 //b->setMeshExpired(true);
5416 catch(InvalidPositionException &e){}
5421 Update mesh of block in which the node is, and if the node is at the
5422 leading edge, update the appropriate leading blocks too.
5424 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5432 v3s16 blockposes[4];
5433 for(u32 i=0; i<4; i++)
5435 v3s16 np = nodepos + dirs[i];
5436 blockposes[i] = getNodeBlockPos(np);
5437 // Don't update mesh of block if it has been done already
5438 bool already_updated = false;
5439 for(u32 j=0; j<i; j++)
5441 if(blockposes[j] == blockposes[i])
5443 already_updated = true;
5450 MapBlock *b = getBlockNoCreate(blockposes[i]);
5451 b->updateMesh(daynight_ratio);
5456 void ClientMap::PrintInfo(std::ostream &out)
5467 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5472 MapVoxelManipulator::~MapVoxelManipulator()
5474 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5478 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5480 TimeTaker timer1("emerge", &emerge_time);
5482 // Units of these are MapBlocks
5483 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5484 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5486 VoxelArea block_area_nodes
5487 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5489 addArea(block_area_nodes);
5491 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5492 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5493 for(s32 x=p_min.X; x<=p_max.X; x++)
5496 core::map<v3s16, bool>::Node *n;
5497 n = m_loaded_blocks.find(p);
5501 bool block_data_inexistent = false;
5504 TimeTaker timer1("emerge load", &emerge_load_time);
5506 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5507 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5510 dstream<<std::endl;*/
5512 MapBlock *block = m_map->getBlockNoCreate(p);
5513 if(block->isDummy())
5514 block_data_inexistent = true;
5516 block->copyTo(*this);
5518 catch(InvalidPositionException &e)
5520 block_data_inexistent = true;
5523 if(block_data_inexistent)
5525 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5526 // Fill with VOXELFLAG_INEXISTENT
5527 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5528 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5530 s32 i = m_area.index(a.MinEdge.X,y,z);
5531 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5535 m_loaded_blocks.insert(p, !block_data_inexistent);
5538 //dstream<<"emerge done"<<std::endl;
5542 SUGG: Add an option to only update eg. water and air nodes.
5543 This will make it interfere less with important stuff if
5546 void MapVoxelManipulator::blitBack
5547 (core::map<v3s16, MapBlock*> & modified_blocks)
5549 if(m_area.getExtent() == v3s16(0,0,0))
5552 //TimeTaker timer1("blitBack");
5554 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5555 <<m_loaded_blocks.size()<<std::endl;*/
5558 Initialize block cache
5560 v3s16 blockpos_last;
5561 MapBlock *block = NULL;
5562 bool block_checked_in_modified = false;
5564 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5565 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5566 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5570 u8 f = m_flags[m_area.index(p)];
5571 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5574 MapNode &n = m_data[m_area.index(p)];
5576 v3s16 blockpos = getNodeBlockPos(p);
5581 if(block == NULL || blockpos != blockpos_last){
5582 block = m_map->getBlockNoCreate(blockpos);
5583 blockpos_last = blockpos;
5584 block_checked_in_modified = false;
5587 // Calculate relative position in block
5588 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5590 // Don't continue if nothing has changed here
5591 if(block->getNode(relpos) == n)
5594 //m_map->setNode(m_area.MinEdge + p, n);
5595 block->setNode(relpos, n);
5598 Make sure block is in modified_blocks
5600 if(block_checked_in_modified == false)
5602 modified_blocks[blockpos] = block;
5603 block_checked_in_modified = true;
5606 catch(InvalidPositionException &e)
5612 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5613 MapVoxelManipulator(map),
5614 m_create_area(false)
5618 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5622 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5624 // Just create the area so that it can be pointed to
5625 VoxelManipulator::emerge(a, caller_id);
5628 void ManualMapVoxelManipulator::initialEmerge(
5629 v3s16 blockpos_min, v3s16 blockpos_max)
5631 TimeTaker timer1("initialEmerge", &emerge_time);
5633 // Units of these are MapBlocks
5634 v3s16 p_min = blockpos_min;
5635 v3s16 p_max = blockpos_max;
5637 VoxelArea block_area_nodes
5638 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5640 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5643 dstream<<"initialEmerge: area: ";
5644 block_area_nodes.print(dstream);
5645 dstream<<" ("<<size_MB<<"MB)";
5649 addArea(block_area_nodes);
5651 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5652 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5653 for(s32 x=p_min.X; x<=p_max.X; x++)
5656 core::map<v3s16, bool>::Node *n;
5657 n = m_loaded_blocks.find(p);
5661 bool block_data_inexistent = false;
5664 TimeTaker timer1("emerge load", &emerge_load_time);
5666 MapBlock *block = m_map->getBlockNoCreate(p);
5667 if(block->isDummy())
5668 block_data_inexistent = true;
5670 block->copyTo(*this);
5672 catch(InvalidPositionException &e)
5674 block_data_inexistent = true;
5677 if(block_data_inexistent)
5680 Mark area inexistent
5682 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5683 // Fill with VOXELFLAG_INEXISTENT
5684 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5685 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5687 s32 i = m_area.index(a.MinEdge.X,y,z);
5688 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5692 m_loaded_blocks.insert(p, !block_data_inexistent);
5696 void ManualMapVoxelManipulator::blitBackAll(
5697 core::map<v3s16, MapBlock*> * modified_blocks)
5699 if(m_area.getExtent() == v3s16(0,0,0))
5703 Copy data of all blocks
5705 for(core::map<v3s16, bool>::Iterator
5706 i = m_loaded_blocks.getIterator();
5707 i.atEnd() == false; i++)
5709 bool existed = i.getNode()->getValue();
5710 if(existed == false)
5712 v3s16 p = i.getNode()->getKey();
5713 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5716 dstream<<"WARNING: "<<__FUNCTION_NAME
5717 <<": got NULL block "
5718 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5723 block->copyFrom(*this);
5726 modified_blocks->insert(p, block);