diff --git a/games/minimal/mods/default/mapgen.lua b/games/minimal/mods/default/mapgen.lua index af94505a0..115bb1458 100644 --- a/games/minimal/mods/default/mapgen.lua +++ b/games/minimal/mods/default/mapgen.lua @@ -27,7 +27,7 @@ minetest.register_alias("mapgen_mese", "default:mese") -- Ore generation -- -local function generate_ore(name, wherein, minp, maxp, seed, chunks_per_volume, ore_per_chunk, height_min, height_max) +local function generate_ore(name, wherein, minp, maxp, seed, chunks_per_volume, ore_per_chunk, height_min, height_max, param2) if maxp.y < height_min or minp.y > height_max then return end @@ -57,7 +57,7 @@ local function generate_ore(name, wherein, minp, maxp, seed, chunks_per_volume, local z2 = z0+z1 local p2 = {x=x2, y=y2, z=z2} if minetest.env:get_node(p2).name == wherein then - minetest.env:set_node(p2, {name=name}) + minetest.env:set_node(p2, {name=name, param2=param2}) end end end @@ -110,5 +110,11 @@ minetest.register_on_generated(function(minp, maxp, seed) end end end + if minetest.setting_get("liquid_finite") then + generate_ore("default:water_source", "default:stone", minp, maxp, seed+42, 1/24/24/24, 4, -100, -10, 128) + generate_ore("default:water_source", "default:stone", minp, maxp, seed+42, 1/28/28/28, 3, -10000, -101, 128) + generate_ore("default:lava_source", "default:stone", minp, maxp, seed+43, 1/38/38/38, 2, -500, -100, 128) + generate_ore("default:lava_source", "default:stone", minp, maxp, seed+43, 1/30/30/30, 4, -31000, -501, 128) + end end) diff --git a/minetest.conf.example b/minetest.conf.example index c36fe05ff..8e95d7e60 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -90,6 +90,14 @@ #enable_fog = true # Enable a bit lower water surface; disable for speed (not quite optimized) #new_style_water = false +# Constant volume liquids +#liquid_finite = false +# Update liquids every .. recommend for finite: 0.2 +#liquid_update = 1.0 +# When finite liquid: relax flowing blocks to source if level near max and N nearby source blocks, more realistic, but not true constant. values: 0,1,2,3,4 : 0 - disable, 1 - most aggresive +#liquid_relax = 1 +# optimization: faster cave flood (and not true constant) +#liquid_fast_flood = 1 # Enable nice leaves; disable for speed #new_style_leaves = true # Enable smooth lighting with simple ambient occlusion; diff --git a/src/content_abm.cpp b/src/content_abm.cpp index 0dcf862fc..971ac4f34 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "mapblock.h" // For getNodeBlockPos #include "treegen.h" // For treegen::make_tree +#include "main.h" // for g_settings #include "map.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -146,11 +147,42 @@ public: } }; +class LiquidFlowABM : public ActiveBlockModifier +{ +private: + std::set contents; + +public: + LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr) + { + std::set liquids; + nodemgr->getIds("group:liquid", liquids); + for(std::set::const_iterator k = liquids.begin(); k != liquids.end(); k++) + contents.insert(nodemgr->get(*k).liquid_alternative_flowing); + + } + virtual std::set getTriggerContents() + { + return contents; + } + virtual float getTriggerInterval() + { return 10.0; } + virtual u32 getTriggerChance() + { return 10; } + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) + { + ServerMap *map = &env->getServerMap(); + if (map->transforming_liquid_size() < 500) + map->transforming_liquid_add(p); + //if ((*map).m_transforming_liquid.size() < 500) (*map).m_transforming_liquid.push_back(p); + } +}; + void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) { env->addActiveBlockModifier(new GrowGrassABM()); env->addActiveBlockModifier(new RemoveGrassABM()); env->addActiveBlockModifier(new MakeTreesFromSaplingsABM()); + if (g_settings->getBool("liquid_finite")) + env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef)); } - - diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 290890490..e224f0b06 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -261,7 +261,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, level = (-0.5+node_liquid_level) * BS; else if(n2.getContent() == c_flowing) level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK) - + 0.5) / 8.0 * node_liquid_level) * BS; + + 0.5) / (float)LIQUID_LEVEL_SOURCE * node_liquid_level) * BS; // Check node above neighbor. // NOTE: This doesn't get executed if neighbor @@ -324,7 +324,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } } if(air_count >= 2) - cornerlevel = -0.5*BS; + cornerlevel = -0.5*BS+0.1; else if(valid_count > 0) cornerlevel /= valid_count; corner_levels[i] = cornerlevel; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 6cdaaa496..f2c9886b4 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -201,6 +201,11 @@ void set_default_settings(Settings *settings) //mapgen related things settings->setDefault("mg_name", "v6"); settings->setDefault("water_level", "1"); + settings->setDefault("liquid_finite", "false"); + settings->setDefault("liquid_update", "1.0"); + settings->setDefault("liquid_relax", "1"); + settings->setDefault("liquid_fast_flood", "1"); + settings->setDefault("chunksize", "5"); settings->setDefault("mg_flags", "trees, caves, v6_biome_blend"); settings->setDefault("mgv6_freq_desert", "0.45"); diff --git a/src/map.cpp b/src/map.cpp index 717b0cf9b..7272451a4 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1613,10 +1613,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 & 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="< must_reflow, must_reflow_second; + + // List of MapBlocks that will require a lighting update (due to lava) + core::map 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="<ndef(); DSTACK(__FUNCTION_NAME); diff --git a/src/map.h b/src/map.h index 420fc29ca..a0305b790 100644 --- a/src/map.h +++ b/src/map.h @@ -300,6 +300,7 @@ public: virtual void PrintInfo(std::ostream &out); void transformLiquids(core::map & modified_blocks); + void transformLiquidsFinite(core::map & modified_blocks); /* Node metadata @@ -328,6 +329,9 @@ public: Variables */ + void transforming_liquid_add(v3s16 p); + s32 transforming_liquid_size(); + protected: std::ostream &m_dout; // A bit deprecated, could be removed diff --git a/src/mapnode.h b/src/mapnode.h index de2529e4d..56dfa6310 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -67,10 +67,15 @@ enum LightBank #define LIQUID_LEVEL_MASK 0x07 #define LIQUID_FLOW_DOWN_MASK 0x08 +//#define LIQUID_LEVEL_MASK 0x3f // better finite water +//#define LIQUID_FLOW_DOWN_MASK 0x40 // not used when finite water + /* maximum amount of liquid in a block */ #define LIQUID_LEVEL_MAX LIQUID_LEVEL_MASK #define LIQUID_LEVEL_SOURCE (LIQUID_LEVEL_MAX+1) +#define LIQUID_INFINITY_MASK 0x80 //0b10000000 + /* This is the stuff what the whole world consists of. */ diff --git a/src/server.cpp b/src/server.cpp index 94a4787f8..138f288c6 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -961,6 +961,7 @@ Server::Server( m_ignore_map_edit_events_peer_id(0) { m_liquid_transform_timer = 0.0; + m_liquid_transform_every = 1.0; m_print_info_timer = 0.0; m_masterserver_timer = 0.0; m_objectdata_timer = 0.0; @@ -1136,6 +1137,8 @@ Server::Server( Add some test ActiveBlockModifiers to environment */ add_legacy_abms(m_env, m_nodedef); + + m_liquid_transform_every = g_settings->getFloat("liquid_update"); } Server::~Server() @@ -1452,9 +1455,9 @@ void Server::AsyncRunStep() /* Transform liquids */ m_liquid_transform_timer += dtime; - if(m_liquid_transform_timer >= 1.00) + if(m_liquid_transform_timer >= m_liquid_transform_every) { - m_liquid_transform_timer -= 1.00; + m_liquid_transform_timer -= m_liquid_transform_every; JMutexAutoLock lock(m_env_mutex); diff --git a/src/server.h b/src/server.h index 22c7cf2bb..16bda4372 100644 --- a/src/server.h +++ b/src/server.h @@ -719,6 +719,7 @@ private: // Some timers float m_liquid_transform_timer; + float m_liquid_transform_every; float m_print_info_timer; float m_masterserver_timer; float m_objectdata_timer;