diff --git a/src/constants.h b/src/constants.h index fc8007602..3cb285065 100644 --- a/src/constants.h +++ b/src/constants.h @@ -97,7 +97,11 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #define MAX_OBJECTDATA_SIZE 450 -#define WATER_LEVEL (0) +/* + This is good to be a bit different than 0 so that water level + is not between to MapBlocks +*/ +#define WATER_LEVEL 3 // Length of cracking animation in count of images #define CRACK_ANIMATION_LENGTH 5 diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index b7b7013d1..c11159f1d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -62,8 +62,8 @@ void set_default_settings() g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); g_settings.setDefault("max_simultaneous_block_sends_server_total", "4"); - g_settings.setDefault("water_moves", "true"); - g_settings.setDefault("disable_water_climb", "true"); + //g_settings.setDefault("water_moves", "true"); + //g_settings.setDefault("disable_water_climb", "true"); //g_settings.setDefault("endless_water", "true"); g_settings.setDefault("max_block_send_distance", "6"); g_settings.setDefault("max_block_generate_distance", "6"); diff --git a/src/light.h b/src/light.h index 269948e86..888b6da50 100644 --- a/src/light.h +++ b/src/light.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define LIGHT_HEADER #include "common_irrlicht.h" +#include "debug.h" /* Day/night cache: @@ -78,7 +79,7 @@ inline u8 decode_light(u8 light) return light_decode_table[LIGHT_MAX]; if(light > LIGHT_MAX) - throw; + light = LIGHT_MAX; return light_decode_table[light]; } diff --git a/src/main.cpp b/src/main.cpp index 61f791f86..3101583cf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -116,12 +116,16 @@ TODO: Startup and configuration menu Graphics: +TODO: + TODO: Optimize day/night mesh updating somehow - create copies of all textures for all lighting values and only change texture for material? - Umm... the collecting of the faces is the slow part -> what about just changing the color values of the existing meshbuffers? It should go quite fast. + - This is not easy; There'd need to be a buffer somewhere + that would contain the night and day lighting values. TODO: Draw big amounts of torches better (that is, throw them in the same meshbuffer (can the meshcollector class be used?)) @@ -129,9 +133,13 @@ TODO: Draw big amounts of torches better (that is, throw them in the TODO: Combine MapBlock's face caches to so big pieces that VBO gets used - That is >500 vertices + - This is not easy; all the MapBlocks close to the player would + still need to be drawn separately and combining the blocks + would have to happen in a background thread TODO: Make fetching sector's blocks more efficient when rendering sectors that have very large amounts of blocks (on client) + - Is this necessary at all? Configuration: @@ -210,6 +218,9 @@ TODO: Map generator version 2 - There could be a certain height (to which mountains only reach) where some minerals are found +FIXME: The new pre-sunlight-propagation code messes up with initial + water lighting + Doing now: ====================================================================== diff --git a/src/map.cpp b/src/map.cpp index 170868f10..612649100 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -926,6 +926,37 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, MapBlock *block = i.getNode()->getValue(); block->updateDayNightDiff(); } + + /* + Add neighboring liquid nodes and the node itself if it is + liquid (=water node was added) to transform queue. + */ + v3s16 dirs[7] = { + v3s16(0,0,0), // self + v3s16(0,0,1), // back + v3s16(0,1,0), // top + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(0,-1,0), // bottom + v3s16(-1,0,0), // left + }; + for(u16 i=0; i<7; i++) + { + try + { + + v3s16 p2 = p + dirs[i]; + + MapNode n2 = getNode(p2); + if(content_liquid(n2.d)) + { + m_transforming_liquid.push_back(p2); + } + + }catch(InvalidPositionException &e) + { + } + } } /* @@ -1063,6 +1094,35 @@ void Map::removeNodeAndUpdate(v3s16 p, MapBlock *block = i.getNode()->getValue(); block->updateDayNightDiff(); } + + /* + Add neighboring liquid nodes to transform queue. + */ + v3s16 dirs[6] = { + v3s16(0,0,1), // back + v3s16(0,1,0), // top + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(0,-1,0), // bottom + v3s16(-1,0,0), // left + }; + for(u16 i=0; i<6; i++) + { + try + { + + v3s16 p2 = p + dirs[i]; + + MapNode n2 = getNode(p2); + if(content_liquid(n2.d)) + { + m_transforming_liquid.push_back(p2); + } + + }catch(InvalidPositionException &e) + { + } + } } #ifndef SERVER @@ -1303,6 +1363,276 @@ void Map::PrintInfo(std::ostream &out) out<<"Map: "; } +#define WATER_DROP_BOOST 4 + +void Map::transformLiquids(core::map & modified_blocks) +{ + DSTACK(__FUNCTION_NAME); + TimeTaker timer("transformLiquids()"); + + u32 loopcount = 0; + u32 initial_size = m_transforming_liquid.size(); + while(m_transforming_liquid.size() != 0) + { + v3s16 p0 = m_transforming_liquid.pop_front(); + + MapNode n0 = getNode(p0); + + // Don't deal with non-liquids + if(content_liquid(n0.d) == false) + continue; + + bool is_source = !content_flowing_liquid(n0.d); + + u8 liquid_level = 8; + if(is_source == false) + liquid_level = n0.param2 & 0x0f; + + // Turn possible source into non-source + u8 nonsource_c = make_liquid_flowing(n0.d); + + /* + If not source, check that some node flows into this one + and what is the level of liquid in this one + */ + if(is_source == false) + { + s8 new_liquid_level_max = -1; + + v3s16 dirs_from[5] = { + v3s16(0,1,0), // top + v3s16(0,0,1), // back + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(-1,0,0), // left + }; + for(u16 i=0; i<5; i++) + { + try + { + + bool from_top = (i==0); + + v3s16 p2 = p0 + dirs_from[i]; + MapNode n2 = getNode(p2); + + if(content_liquid(n2.d)) + { + u8 n2_nonsource_c = make_liquid_flowing(n2.d); + // Check that the liquids are the same type + if(n2_nonsource_c != nonsource_c) + { + dstream<<"WARNING: Not handling: different liquids" + " collide"<= 7 - WATER_DROP_BOOST) + new_liquid_level = 7; + else + new_liquid_level = n2_liquid_level + WATER_DROP_BOOST; + } + else if(n2_liquid_level > 0) + { + new_liquid_level = n2_liquid_level - 1; + } + + if(new_liquid_level > new_liquid_level_max) + new_liquid_level_max = new_liquid_level; + } + + }catch(InvalidPositionException &e) + { + } + } //for + + /* + If liquid level should be something else, update it and + add all the neighboring water nodes to the transform queue. + */ + if(new_liquid_level_max != liquid_level) + { + if(new_liquid_level_max == -1) + { + // Remove water alltoghether + n0.d = CONTENT_AIR; + n0.param2 = 0; + setNode(p0, n0); + } + else + { + n0.param2 = new_liquid_level_max; + setNode(p0, n0); + } + + // Block has been modified + { + v3s16 blockpos = getNodeBlockPos(p0); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block != NULL) + modified_blocks.insert(blockpos, block); + } + + /* + Add neighboring non-source liquid nodes to transform queue. + */ + v3s16 dirs[6] = { + v3s16(0,0,1), // back + v3s16(0,1,0), // top + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(0,-1,0), // bottom + v3s16(-1,0,0), // left + }; + for(u16 i=0; i<6; i++) + { + try + { + + v3s16 p2 = p0 + dirs[i]; + + MapNode n2 = getNode(p2); + if(content_flowing_liquid(n2.d)) + { + m_transforming_liquid.push_back(p2); + } + + }catch(InvalidPositionException &e) + { + } + } + } + } + + // Get a new one from queue if the node has turned into non-water + if(content_liquid(n0.d) == false) + continue; + + /* + Flow water from this node + */ + v3s16 dirs_to[5] = { + v3s16(0,-1,0), // bottom + v3s16(0,0,1), // back + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(-1,0,0), // left + }; + for(u16 i=0; i<5; i++) + { + try + { + + bool to_bottom = (i == 0); + + // If liquid is at lowest possible height, it's not going + // anywhere except down + if(liquid_level == 0 && to_bottom == false) + continue; + + u8 liquid_next_level = 0; + // If going to bottom + if(to_bottom) + { + //liquid_next_level = 7; + if(liquid_level >= 7 - WATER_DROP_BOOST) + liquid_next_level = 7; + else + liquid_next_level = liquid_level + WATER_DROP_BOOST; + } + else + liquid_next_level = liquid_level - 1; + + bool n2_changed = false; + bool flowed = false; + + v3s16 p2 = p0 + dirs_to[i]; + + MapNode n2 = getNode(p2); + + if(content_liquid(n2.d)) + { + u8 n2_nonsource_c = make_liquid_flowing(n2.d); + // Check that the liquids are the same type + if(n2_nonsource_c != nonsource_c) + { + dstream<<"WARNING: Not handling: different liquids" + " collide"< liquid_level) + { + n2.param2 = liquid_next_level; + setNode(p2, n2); + + n2_changed = true; + flowed = true; + } + } + } + else if(n2.d == CONTENT_AIR) + { + n2.d = nonsource_c; + n2.param2 = liquid_next_level; + setNode(p2, n2); + + n2_changed = true; + flowed = true; + } + + if(n2_changed) + { + m_transforming_liquid.push_back(p2); + + v3s16 blockpos = getNodeBlockPos(p2); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block != NULL) + modified_blocks.insert(blockpos, block); + } + + // If n2_changed to bottom, don't flow anywhere else + if(to_bottom && flowed) + break; + + }catch(InvalidPositionException &e) + { + } + } + + loopcount++; + //if(loopcount >= 100000) + if(loopcount >= initial_size * 1) + break; + } + dstream<<"Map::transformLiquids(): loopcount="<getNearAttr(nodepos2d).getFloat();*/ - float local_plants_amount = - palist->getInterpolatedFloat(nodepos2d); - - //dstream<<"emergeSector(): done."<getNearAttr(nodepos2d).getFloat();*/ + local_plants_amount = + palist->getInterpolatedFloat(nodepos2d); + } /* Generate sector heightmap @@ -1988,6 +2325,12 @@ MapBlock * ServerMap::emergeBlock( n.d = water_material; n.setLight(LIGHTBANK_DAY, diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1)); + /* + Add to transforming liquid queue (in case it'd + start flowing) + */ + v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE; + m_transforming_liquid.push_back(real_pos); } // else air else @@ -2692,7 +3035,8 @@ continue_generating: core::map light_sources; bool black_air_left = false; bool bottom_invalid = - block->propagateSunlight(light_sources, true, &black_air_left); + block->propagateSunlight(light_sources, true, + &black_air_left, true); // If sunlight didn't reach everywhere and part of block is // above ground, lighting has to be properly updated @@ -2700,6 +3044,11 @@ continue_generating: { lighting_invalidated_blocks[block->getPos()] = block; } + + if(bottom_invalid) + { + lighting_invalidated_blocks[block->getPos()] = block; + } } /* @@ -2739,7 +3088,7 @@ continue_generating: if(haxmode) { // Don't calculate lighting at all - lighting_invalidated_blocks.clear(); + //lighting_invalidated_blocks.clear(); } return block; @@ -3523,11 +3872,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Total distance f32 d = blockpos_relative.getLength(); - - /* - Draw the faces of the block - */ + #if 1 + /* + Update expired mesh + */ + bool mesh_expired = false; { @@ -3585,6 +3935,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) continue; }*/ #endif + /* + Draw the faces of the block + */ { JMutexAutoLock lock(block->mesh_mutex); diff --git a/src/map.h b/src/map.h index 5cee3b7c0..3b6b169e5 100644 --- a/src/map.h +++ b/src/map.h @@ -236,6 +236,8 @@ public: // For debug printing virtual void PrintInfo(std::ostream &out); + + void transformLiquids(core::map & modified_blocks); /* Variables diff --git a/src/mapblock.cpp b/src/mapblock.cpp index cf9114b20..8de81b774 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -146,6 +146,25 @@ u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, v3s16 face_dir) { try{ + // DEBUG + /*{ + if(n.d == CONTENT_WATER) + { + u8 l = n.param2*2; + if(l > LIGHT_MAX) + l = LIGHT_MAX; + return l; + } + if(n2.d == CONTENT_WATER) + { + u8 l = n2.param2*2; + if(l > LIGHT_MAX) + l = LIGHT_MAX; + return l; + } + }*/ + + u8 light; u8 l1 = n.getLightBlend(daynight_ratio); u8 l2 = n2.getLightBlend(daynight_ratio); @@ -645,10 +664,10 @@ void MapBlock::updateMesh(u32 daynight_ratio) mesh_new = new scene::SMesh(); + MeshCollector collector; + if(fastfaces_new.size() > 0) { - MeshCollector collector; - for(u32 i=0; isetHardwareMappingHint(scene::EHM_STATIC); - - /*std::cout<<"MapBlock has "<getMeshBufferCount() - <<" materials (meshbuffers)"<append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - buf->getMaterial().MaterialType + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; if(dir == v3s16(0,-1,0)) - buf->getMaterial().setTexture(0, + material.setTexture(0, g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str())); else if(dir == v3s16(0,1,0)) - buf->getMaterial().setTexture(0, + material.setTexture(0, g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str())); // For backwards compatibility else if(dir == v3s16(0,0,0)) - buf->getMaterial().setTexture(0, + material.setTexture(0, g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str())); else - buf->getMaterial().setTexture(0, + material.setTexture(0, g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str())); - // Add to mesh - mesh_new->addMeshBuffer(buf); - buf->drop(); + u16 indices[] = {0,1,2,2,3,0}; + // Add to mesh collector + collector.append(material, vertices, 4, indices, 6); + } + else if(n.d == CONTENT_WATER) + { + bool top_is_water = false; + try{ + MapNode n = getNodeParent(v3s16(x,y+1,z)); + if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE) + top_is_water = true; + }catch(InvalidPositionException &e){} + + video::SColor c(128,255,255,255); + + // Neighbor water levels (key = relative position) + // Includes current node + core::map neighbor_levels; + core::map neighbor_contents; + v3s16 neighbor_dirs[9] = { + v3s16(0,0,0), + v3s16(0,0,1), + v3s16(0,0,-1), + v3s16(1,0,0), + v3s16(-1,0,0), + v3s16(1,0,1), + v3s16(-1,0,-1), + v3s16(1,0,-1), + v3s16(-1,0,1), + }; + for(u32 i=0; i<9; i++) + { + u8 content = CONTENT_AIR; + float level = -0.5 * BS; + try{ + v3s16 p2 = p + neighbor_dirs[i]; + MapNode n2 = getNodeParent(p2); + + content = n2.d; + + if(n2.d == CONTENT_WATERSOURCE) + level = 0.5 * BS; + else if(n2.d == CONTENT_WATER) + level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS; + } + catch(InvalidPositionException &e){} + + neighbor_levels.insert(neighbor_dirs[i], level); + neighbor_contents.insert(neighbor_dirs[i], content); + } + + //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS; + //float water_level = neighbor_levels[v3s16(0,0,0)]; + + // Corner heights (average between four waters) + f32 corner_levels[4]; + + v3s16 halfdirs[4] = { + v3s16(0,0,0), + v3s16(1,0,0), + v3s16(1,0,1), + v3s16(0,0,1), + }; + for(u32 i=0; i<4; i++) + { + v3s16 cornerdir = halfdirs[i]; + float cornerlevel = 0; + u32 valid_count = 0; + for(u32 j=0; j<4; j++) + { + v3s16 neighbordir = cornerdir - halfdirs[j]; + u8 content = neighbor_contents[neighbordir]; + // Special case for source nodes + if(content == CONTENT_WATERSOURCE) + { + cornerlevel = 0.5*BS; + valid_count = 1; + break; + } + else if(content == CONTENT_WATER) + { + cornerlevel += neighbor_levels[neighbordir]; + valid_count++; + } + else if(content == CONTENT_AIR) + { + cornerlevel += -0.5*BS; + valid_count++; + } + } + if(valid_count > 0) + cornerlevel /= valid_count; + corner_levels[i] = cornerlevel; + } + + /* + Generate sides + */ + + v3s16 side_dirs[4] = { + v3s16(1,0,0), + v3s16(-1,0,0), + v3s16(0,0,1), + v3s16(0,0,-1), + }; + s16 side_corners[4][2] = { + {1, 2}, + {3, 0}, + {2, 3}, + {0, 1}, + }; + for(u32 i=0; i<4; i++) + { + v3s16 dir = side_dirs[i]; + + //float neighbor_level = neighbor_levels[dir]; + /*if(neighbor_level > -0.5*BS + 0.001) + continue;*/ + /*if(neighbor_level > water_level - 0.1*BS) + continue;*/ + + u8 neighbor_content = neighbor_contents[dir]; + + if(neighbor_content != CONTENT_AIR + && neighbor_content != CONTENT_WATER) + continue; + + bool neighbor_is_water = (neighbor_content == CONTENT_WATER); + + if(neighbor_is_water == true && top_is_water == false) + continue; + + video::S3DVertex vertices[4] = + { + /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1), + video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1), + video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/ + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + }; + + if(top_is_water) + { + vertices[2].Pos.Y = 0.5*BS; + vertices[3].Pos.Y = 0.5*BS; + } + else + { + vertices[2].Pos.Y = corner_levels[side_corners[i][0]]; + vertices[3].Pos.Y = corner_levels[side_corners[i][1]]; + } + + if(neighbor_is_water) + { + vertices[0].Pos.Y = corner_levels[side_corners[i][1]]; + vertices[1].Pos.Y = corner_levels[side_corners[i][0]]; + } + else + { + vertices[0].Pos.Y = -0.5*BS; + vertices[1].Pos.Y = -0.5*BS; + } + + for(s32 j=0; j<4; j++) + { + if(dir == v3s16(0,0,1)) + vertices[j].Pos.rotateXZBy(0); + if(dir == v3s16(0,0,-1)) + vertices[j].Pos.rotateXZBy(180); + if(dir == v3s16(-1,0,0)) + vertices[j].Pos.rotateXZBy(90); + if(dir == v3s16(1,0,-0)) + vertices[j].Pos.rotateXZBy(-90); + + vertices[j].Pos += intToFloat(p + getPosRelative()); + } + + // Set material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; + material.setTexture(0, + g_irrlicht->getTexture(porting::getDataPath("water.png").c_str())); + + u16 indices[] = {0,1,2,2,3,0}; + // Add to mesh collector + collector.append(material, vertices, 4, indices, 6); + } + + /* + Generate top side, if appropriate + */ + + if(top_is_water == false) + { + video::S3DVertex vertices[4] = + { + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + }; + + for(s32 i=0; i<4; i++) + { + //vertices[i].Pos.Y += water_level; + //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)]; + vertices[i].Pos.Y += corner_levels[i]; + vertices[i].Pos += intToFloat(p + getPosRelative()); + } + + // Set material + video::SMaterial material; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; + material.setTexture(0, + g_irrlicht->getTexture(porting::getDataPath("water.png").c_str())); + + u16 indices[] = {0,1,2,2,3,0}; + // Add to mesh collector + collector.append(material, vertices, 4, indices, 6); + } } } + + /* + Add stuff from collector to mesh + */ + collector.fillMesh(mesh_new); + /* Do some stuff to the mesh */ @@ -792,6 +1029,14 @@ void MapBlock::updateMesh(u32 daynight_ratio) mesh_new = NULL; } + // Use VBO for mesh (this just would set this for ever buffer) + // This will lead to infinite memory usage because or irrlicht. + //mesh_new->setHardwareMappingHint(scene::EHM_STATIC); + + /*std::cout<<"MapBlock has "<getMeshBufferCount() + <<" materials (meshbuffers)"< & light_sources, - bool remove_light, bool *black_air_left) + bool remove_light, bool *black_air_left, + bool grow_grass) { // Whether the sunlight at the top of the bottom block is valid bool block_below_is_valid = true; @@ -962,10 +1208,23 @@ bool MapBlock::propagateSunlight(core::map & light_sources, } else if(n.light_propagates() == false) { - // Turn mud into grass - if(n.d == CONTENT_MUD && current_light == LIGHT_SUN) + if(grow_grass) { - n.d = CONTENT_GRASS; + bool upper_is_air = false; + try + { + if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR) + upper_is_air = true; + } + catch(InvalidPositionException &e) + { + } + // Turn mud into grass + if(upper_is_air && n.d == CONTENT_MUD + && current_light == LIGHT_SUN) + { + n.d = CONTENT_GRASS; + } } // A solid object is on the way. diff --git a/src/mapblock.h b/src/mapblock.h index e4f93a031..dd5277668 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -315,7 +315,8 @@ public: // See comments in mapblock.cpp bool propagateSunlight(core::map & light_sources, - bool remove_light=false, bool *black_air_left=NULL); + bool remove_light=false, bool *black_air_left=NULL, + bool grow_grass=false); // Copies data to VoxelManipulator to getPosRelative() void copyTo(VoxelManipulator &dst); diff --git a/src/mapnode.cpp b/src/mapnode.cpp index de6953b78..59e40935c 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -35,7 +35,8 @@ u16 g_content_tiles[USEFUL_CONTENT_COUNT][6] = { {TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE,TILE_STONE}, {TILE_GRASS,TILE_MUD,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS,TILE_MUD_WITH_GRASS}, - {TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER}, + //{TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER,TILE_WATER}, + {TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE}, {TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE,TILE_NONE}, {TILE_TREE_TOP,TILE_TREE_TOP,TILE_TREE,TILE_TREE,TILE_TREE,TILE_TREE}, {TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES,TILE_LEAVES}, diff --git a/src/mapnode.h b/src/mapnode.h index a759f807e..e3b921a66 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -108,7 +108,7 @@ inline bool sunlight_propagates_content(u8 m) inline u8 content_solidness(u8 m) { // As of now, every pseudo node like torches are added to this - if(m == CONTENT_AIR || m == CONTENT_TORCH) + if(m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER) return 0; if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE) return 1; @@ -121,12 +121,30 @@ inline bool content_walkable(u8 m) return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH); } -// A liquid resists fast movement inline bool content_liquid(u8 m) { return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE); } +inline bool content_flowing_liquid(u8 m) +{ + return (m == CONTENT_WATER); +} + +inline bool content_liquid_source(u8 m) +{ + return (m == CONTENT_WATERSOURCE); +} + +// CONTENT_WATER || CONTENT_WATERSOURCE -> CONTENT_WATER +// CONTENT_LAVA || CONTENT_LAVASOURCE -> CONTENT_LAVA +inline u8 make_liquid_flowing(u8 m) +{ + if(m == CONTENT_WATER || m == CONTENT_WATERSOURCE) + return CONTENT_WATER; + assert(0); +} + // Pointable contents can be pointed to in the map inline bool content_pointable(u8 m) { @@ -349,6 +367,8 @@ struct MapNode union { + u8 param2; + /* Pressure for liquids */ diff --git a/src/server.cpp b/src/server.cpp index 312e2b6d9..17004a803 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -971,7 +971,8 @@ Server::Server( m_time_of_day_send_timer(0), m_uptime(0) { - m_flowwater_timer = 0.0; + //m_flowwater_timer = 0.0; + m_liquid_transform_timer = 0.0; m_print_info_timer = 0.0; m_objectdata_timer = 0.0; m_emergethread_trigger_timer = 0.0; @@ -1140,9 +1141,54 @@ void Server::AsyncRunStep() /* Do background stuff */ - + + /* + Transform liquids + */ + m_liquid_transform_timer += dtime; + if(m_liquid_transform_timer >= 1.00) { - //m_env.getMap(). + m_liquid_transform_timer -= 1.00; + + JMutexAutoLock lock(m_env_mutex); + + core::map modified_blocks; + m_env.getMap().transformLiquids(modified_blocks); +#if 0 + /* + Update lighting + */ + core::map lighting_modified_blocks; + ServerMap &map = ((ServerMap&)m_env.getMap()); + map.updateLighting(modified_blocks, lighting_modified_blocks); + + // Add blocks modified by lighting to modified_blocks + for(core::map::Iterator + i = lighting_modified_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + modified_blocks.insert(block->getPos(), block); + } +#endif + /* + Set the modified blocks unsent for all the clients + */ + + JMutexAutoLock lock2(m_con_mutex); + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + + if(modified_blocks.size() > 0) + { + // Remove block from sent history + client->SetBlocksNotSent(modified_blocks); + } + } } #if 0 diff --git a/src/server.h b/src/server.h index a1c4a1cd7..4bdaa8455 100644 --- a/src/server.h +++ b/src/server.h @@ -457,7 +457,8 @@ private: void handlePeerChange(PeerChange &c); void handlePeerChanges(); - float m_flowwater_timer; + //float m_flowwater_timer; + float m_liquid_transform_timer; float m_print_info_timer; float m_objectdata_timer; float m_emergethread_trigger_timer; diff --git a/src/utility.h b/src/utility.h index a38d15f30..897390dba 100644 --- a/src/utility.h +++ b/src/utility.h @@ -1608,7 +1608,7 @@ public: return true; } - void pop_front() + Value pop_front() { typename core::list::Iterator i = m_list.begin(); Value value = *i; @@ -1617,6 +1617,12 @@ public: return value; } + u32 size() + { + assert(m_list.size() == m_map.size()); + return m_list.size(); + } + private: core::map m_map; core::list m_list;