diff options
Diffstat (limited to 'src/map.cpp')
-rw-r--r-- | src/map.cpp | 404 |
1 files changed, 369 insertions, 35 deletions
diff --git a/src/map.cpp b/src/map.cpp index 717b0cf9b..4be094326 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,6 +1,6 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "util/directiontables.h" #include "rollback_interface.h" +#include "emerge.h" #include "mapgen_v6.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -1613,10 +1614,322 @@ struct NodeNeighbor { MapNode n; NeighborType t; v3s16 p; + bool l; //can liquid + bool i; //infinity }; +void Map::transforming_liquid_add(v3s16 p) { + m_transforming_liquid.push_back(p); +} + +s32 Map::transforming_liquid_size() { + return m_transforming_liquid.size(); +} + +const v3s16 g_7dirs[7] = +{ + // +right, +top, +back + v3s16( 0,-1, 0), // bottom + v3s16( 0, 0, 0), // self + v3s16( 0, 0, 1), // back + v3s16( 0, 0,-1), // front + v3s16( 1, 0, 0), // right + v3s16(-1, 0, 0), // left + v3s16( 0, 1, 0) // top +}; + +#define D_BOTTOM 0 +#define D_TOP 6 +#define D_SELF 1 + +void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) +{ + INodeDefManager *nodemgr = m_gamedef->ndef(); + + DSTACK(__FUNCTION_NAME); + //TimeTaker timer("transformLiquids()"); + + u32 loopcount = 0; + u32 initial_size = m_transforming_liquid.size(); + + u8 relax = g_settings->getS16("liquid_relax"); + bool fast_flood = g_settings->getS16("liquid_fast_flood"); + int water_level = g_settings->getS16("water_level"); + + /*if(initial_size != 0) + infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/ + + // list of nodes that due to viscosity have not reached their max level height + UniqueQueue<v3s16> must_reflow, must_reflow_second; + + // List of MapBlocks that will require a lighting update (due to lava) + core::map<v3s16, MapBlock*> lighting_modified_blocks; + + while(m_transforming_liquid.size() > 0) + { + // This should be done here so that it is done when continue is used + if(loopcount >= initial_size || loopcount >= 1000) + break; + loopcount++; + /* + Get a queued transforming liquid node + */ + v3s16 p0 = m_transforming_liquid.pop_front(); + u16 total_level = 0; + NodeNeighbor neighbors[7]; // surrounding flowing liquid nodes + s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1}; // current level of every block + s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; // target levels + s8 can_liquid_same_level = 0; + content_t liquid_kind = CONTENT_IGNORE; + content_t liquid_kind_flowing = CONTENT_IGNORE; + /* + Collect information about the environment + */ + const v3s16 *dirs = g_7dirs; + for (u16 i = 0; i < 7; i++) { + NeighborType nt = NEIGHBOR_SAME_LEVEL; + switch (i) { + case D_TOP: + nt = NEIGHBOR_UPPER; + break; + case D_BOTTOM: + nt = NEIGHBOR_LOWER; + break; + } + v3s16 npos = p0 + dirs[i]; + + neighbors[i].n = getNodeNoEx(npos); + neighbors[i].t = nt; + neighbors[i].p = npos; + neighbors[i].l = 0; + neighbors[i].i = 0; + NodeNeighbor & nb = neighbors[i]; + + switch (nodemgr->get(nb.n.getContent()).liquid_type) { + case LIQUID_NONE: + if (nb.n.getContent() == CONTENT_AIR) { + liquid_levels[i] = 0; + nb.l = 1; + } + break; + case LIQUID_SOURCE: + // if this node is not (yet) of a liquid type, choose the first liquid type we encounter + if (liquid_kind_flowing == CONTENT_IGNORE) + liquid_kind_flowing = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing); + if (liquid_kind == CONTENT_IGNORE) + liquid_kind = nb.n.getContent(); + if (nb.n.getContent() == liquid_kind) { + liquid_levels[i] = LIQUID_LEVEL_SOURCE; + nb.l = 1; + nb.i = (nb.n.param2 & LIQUID_INFINITY_MASK); + } + break; + case LIQUID_FLOWING: + // if this node is not (yet) of a liquid type, choose the first liquid type we encounter + if (liquid_kind_flowing == CONTENT_IGNORE) + liquid_kind_flowing = nb.n.getContent(); + if (liquid_kind == CONTENT_IGNORE) + liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_source); + if (nb.n.getContent() == liquid_kind_flowing) { + liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK); + nb.l = 1; + } + break; + } + if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) ++can_liquid_same_level; + if (liquid_levels[i] > 0) total_level += liquid_levels[i]; + + /* + infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="<<nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="<<nodemgr->get(nb.n.getContent()).liquid_type + //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing + << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i] << " tlevel=" << (int)total_level << " cansame="<<(int)can_liquid_same_level<<std::endl; + */ + } + + if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].l || total_level <= 0) + continue; + + // fill bottom block + if (neighbors[D_BOTTOM].l) { + liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level; + total_level -= liquid_levels_want[D_BOTTOM]; + } + + if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 && total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level - can_liquid_same_level + 2 && can_liquid_same_level >= relax + 1) { //relax up + total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level; + } + + // calculate self level 5 blocks + u8 want_level = + total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level + ? LIQUID_LEVEL_SOURCE + : total_level / can_liquid_same_level; + total_level -= want_level * can_liquid_same_level; + + if (relax && p0.Y > water_level && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && total_level <= can_liquid_same_level - 2 && can_liquid_same_level >= relax + 1) { //relax down + total_level = 0; + } + + for (u16 ii = D_SELF; ii < D_TOP; ++ii) { // fill only same level + if (!neighbors[ii].l) + continue; + liquid_levels_want[ii] = want_level; + if (liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE && total_level > 0 + && liquid_levels[ii] > liquid_levels_want[ii] + ) { + ++liquid_levels_want[ii]; + --total_level; + } + } + + for (u16 ii = 0; ii < 7; ++ii) { + if (total_level < 1) break; + if (liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) { + ++liquid_levels_want[ii]; + --total_level; + } + } + + // fill top block if can + if (neighbors[D_TOP].l) { + liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ; + total_level -= liquid_levels_want[D_TOP]; + } + + for (u16 ii = 0; ii < 7; ii++) // infinity and cave flood optimization + if (liquid_levels_want[ii] >= 0 && + (neighbors[ii].i || + (fast_flood && p0.Y < water_level && + (initial_size >= 1000 + && ii != D_TOP + && want_level >= LIQUID_LEVEL_SOURCE/4 + && can_liquid_same_level >= 5 + && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE)))) + liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE; + + //if (total_level > 0 /*|| flowed != volume*/) infostream <<" AFTER level=" << (int)total_level /*<< " flowed="<<flowed<< " volume=" <<volume*/<< " wantsame="<<(int)want_level<< " top="<< (int)liquid_levels_want[D_TOP]<< " topwas="<< (int)liquid_levels[D_TOP]<< " bot="<< (int)liquid_levels_want[D_BOTTOM]<<std::endl; + + u8 changed = 0; + for (u16 i = 0; i < 7; i++) { + if (liquid_levels_want[i] < 0 || !neighbors[i].l) + continue; + MapNode & n0 = neighbors[i].n; + p0 = neighbors[i].p; + /* + decide on the type (and possibly level) of the current node + */ + content_t new_node_content; + s8 new_node_level = -1; + u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity; + if (viscosity > 1 && liquid_levels_want[i] != liquid_levels[i]) { + // amount to gain, limited by viscosity + // must be at least 1 in absolute value + s8 level_inc = liquid_levels_want[i] - liquid_levels[i]; + if (level_inc < -viscosity || level_inc > viscosity) + new_node_level = liquid_levels[i] + level_inc/viscosity; + else if (level_inc < 0) + new_node_level = liquid_levels[i] - 1; + else if (level_inc > 0) + new_node_level = liquid_levels[i] + 1; + } else + new_node_level = liquid_levels_want[i]; + if (new_node_level >= LIQUID_LEVEL_SOURCE) + new_node_content = liquid_kind; + else if (new_node_level > 0) + new_node_content = liquid_kind_flowing; + else + new_node_content = CONTENT_AIR; + + // last level must flow down on stairs + if (liquid_levels_want[i] != liquid_levels[i] && liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l && new_node_level >= 1 && new_node_level <= 2) //maybe == 1 // + for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level + if (!neighbors[ii].l) + continue; + must_reflow_second.push_back(p0 + dirs[ii]); + } + + /* + check if anything has changed. if not, just continue with the next node. + */ + if ( + new_node_content == n0.getContent() + && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING || + ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level + // &&((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)== flowing_down + )) + && + (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE || + (((n0.param2 & LIQUID_INFINITY_MASK) == LIQUID_INFINITY_MASK) == neighbors[i].i + )) + ) { + continue; + } + ++changed; + + /* + update the current node + */ + if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) { + // set level to last 3 bits, flowing down bit to 4th bit + n0.param2 = (new_node_level & LIQUID_LEVEL_MASK); + } else if (nodemgr->get(new_node_content).liquid_type == LIQUID_SOURCE) { + //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK); + n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00); + } + //infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="<<new_node_content<< " p2="<<(int)n0.param2<< " nl="<<(int)new_node_level<<std::endl; + n0.setContent(new_node_content); + // Find out whether there is a suspect for this action + std::string suspect; + if(m_gamedef->rollback()){ + suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1); + } + + if(!suspect.empty()){ + // Blame suspect + RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true); + // Get old node for rollback + RollbackNode rollback_oldnode(this, p0, m_gamedef); + // Set node + setNode(p0, n0); + // Report + RollbackNode rollback_newnode(this, p0, m_gamedef); + RollbackAction action; + action.setSetNode(p0, rollback_oldnode, rollback_newnode); + m_gamedef->rollback()->reportAction(action); + } else { + // Set node + setNode(p0, n0); + } + + v3s16 blockpos = getNodeBlockPos(p0); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block != NULL) { + modified_blocks.insert(blockpos, block); + // If node emits light, MapBlock requires lighting update + if(nodemgr->get(n0).light_source != 0) + lighting_modified_blocks[block->getPos()] = block; + } + must_reflow.push_back(neighbors[i].p); + } + /* //for better relax + if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level + if (!neighbors[ii].l) continue; + must_reflow.push_back(p0 + dirs[ii]); + }*/ + } + //if (loopcount) infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" reflow="<<must_reflow.size()<<" queue="<< m_transforming_liquid.size()<<std::endl; + while (must_reflow.size() > 0) + m_transforming_liquid.push_back(must_reflow.pop_front()); + while (must_reflow_second.size() > 0) + m_transforming_liquid.push_back(must_reflow_second.pop_front()); + updateLighting(lighting_modified_blocks, modified_blocks); +} + void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks) { + + if (g_settings->getBool("liquid_finite")) return Map::transformLiquidsFinite(modified_blocks); + INodeDefManager *nodemgr = m_gamedef->ndef(); DSTACK(__FUNCTION_NAME); @@ -2008,7 +2321,7 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer m_mgparams = m_emerge->getParamsFromSettings(g_settings); if (!m_mgparams) m_mgparams = new MapgenV6Params(); - + m_seed = m_mgparams->seed; if (g_settings->get("fixed_map_seed").empty()) @@ -2142,14 +2455,10 @@ ServerMap::~ServerMap() #endif } -void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) +bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) { - bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); - if(enable_mapgen_debug_info) - infostream<<"initBlockMake(): " - <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - " - <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")" - <<std::endl; + bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info; + EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos)); //s16 chunksize = 3; //v3s16 chunk_offset(-1,-1,-1); @@ -2169,12 +2478,8 @@ void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) // Do nothing if not inside limits (+-1 because of neighbors) if(blockpos_over_limit(blockpos_min - extra_borders) || blockpos_over_limit(blockpos_max + extra_borders)) - { - data->no_op = true; - return; - } + return false; - data->no_op = false; data->seed = m_seed; data->blockpos_min = blockpos_min; data->blockpos_max = blockpos_max; @@ -2245,8 +2550,24 @@ void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) //TimeTaker timer("initBlockMake() initialEmerge"); data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max); } + + // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE +/* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) { + for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) { + for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) { + core::map<v3s16, u8>::Node *n; + n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z)); + if (n == NULL) + continue; + u8 flags = n->getValue(); + flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE; + n->setValue(flags); + } + } + }*/ // Data is ready now. + return true; } MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, @@ -2261,13 +2582,7 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, v3s16 extra_borders(1,1,1); - if(data->no_op) - { - //infostream<<"finishBlockMake(): no-op"<<std::endl; - return NULL; - } - - bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info"); + bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info; /*infostream<<"Resulting vmanip:"<<std::endl; data->vmanip.print(infostream);*/ @@ -2295,9 +2610,7 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, data->vmanip->blitBackAll(&changed_blocks); } - if(enable_mapgen_debug_info) - infostream<<"finishBlockMake: changed_blocks.size()=" - <<changed_blocks.size()<<std::endl; + EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size()); /* Copy transforming liquid information @@ -2970,7 +3283,7 @@ void ServerMap::save(ModifiedState save_level) block_count_all++; - if(block->getModified() >= save_level) + if(block->getModified() >= (u32)save_level) { // Lazy beginSave() if(!save_started){ @@ -3671,8 +3984,10 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) for(s32 y=p_min.Y; y<=p_max.Y; y++) for(s32 x=p_min.X; x<=p_max.X; x++) { + u8 flags = 0; + MapBlock *block; v3s16 p(x,y,z); - core::map<v3s16, bool>::Node *n; + core::map<v3s16, u8>::Node *n; n = m_loaded_blocks.find(p); if(n != NULL) continue; @@ -3688,7 +4003,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) a.print(infostream); infostream<<std::endl;*/ - MapBlock *block = m_map->getBlockNoCreate(p); + block = m_map->getBlockNoCreate(p); if(block->isDummy()) block_data_inexistent = true; else @@ -3701,6 +4016,8 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) if(block_data_inexistent) { + flags |= VMANIP_BLOCK_DATA_INEXIST; + VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); // Fill with VOXELFLAG_INEXISTENT for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) @@ -3710,8 +4027,13 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE); } } + /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE) + { + // Mark that block was loaded as blank + flags |= VMANIP_BLOCK_CONTAINS_CIGNORE; + }*/ - m_loaded_blocks.insert(p, !block_data_inexistent); + m_loaded_blocks.insert(p, flags); } //infostream<<"emerge done"<<std::endl; @@ -3831,8 +4153,10 @@ void ManualMapVoxelManipulator::initialEmerge( for(s32 y=p_min.Y; y<=p_max.Y; y++) for(s32 x=p_min.X; x<=p_max.X; x++) { + u8 flags = 0; + MapBlock *block; v3s16 p(x,y,z); - core::map<v3s16, bool>::Node *n; + core::map<v3s16, u8>::Node *n; n = m_loaded_blocks.find(p); if(n != NULL) continue; @@ -3842,7 +4166,7 @@ void ManualMapVoxelManipulator::initialEmerge( { TimeTaker timer1("emerge load", &emerge_load_time); - MapBlock *block = m_map->getBlockNoCreate(p); + block = m_map->getBlockNoCreate(p); if(block->isDummy()) block_data_inexistent = true; else @@ -3855,6 +4179,8 @@ void ManualMapVoxelManipulator::initialEmerge( if(block_data_inexistent) { + flags |= VMANIP_BLOCK_DATA_INEXIST; + /* Mark area inexistent */ @@ -3867,8 +4193,13 @@ void ManualMapVoxelManipulator::initialEmerge( memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE); } } + /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE) + { + // Mark that block was loaded as blank + flags |= VMANIP_BLOCK_CONTAINS_CIGNORE; + }*/ - m_loaded_blocks.insert(p, !block_data_inexistent); + m_loaded_blocks.insert(p, flags); } } @@ -3881,12 +4212,14 @@ void ManualMapVoxelManipulator::blitBackAll( /* Copy data of all blocks */ - for(core::map<v3s16, bool>::Iterator + for(core::map<v3s16, u8>::Iterator i = m_loaded_blocks.getIterator(); i.atEnd() == false; i++) { v3s16 p = i.getNode()->getKey(); - bool existed = i.getNode()->getValue(); + u8 flags = i.getNode()->getValue(); + + bool existed = !(flags & VMANIP_BLOCK_DATA_INEXIST); if(existed == false) { // The Great Bug was found using this @@ -3895,6 +4228,7 @@ void ManualMapVoxelManipulator::blitBackAll( <<std::endl;*/ continue; } + MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block == NULL) { @@ -3906,7 +4240,7 @@ void ManualMapVoxelManipulator::blitBackAll( } block->copyFrom(*this); - + if(modified_blocks) modified_blocks->insert(p, block); } |