aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content_abm.cpp36
-rw-r--r--src/content_mapblock.cpp4
-rw-r--r--src/defaultsettings.cpp5
-rw-r--r--src/map.cpp312
-rw-r--r--src/map.h4
-rw-r--r--src/mapnode.h5
-rw-r--r--src/server.cpp7
-rw-r--r--src/server.h1
8 files changed, 368 insertions, 6 deletions
diff --git a/src/content_abm.cpp b/src/content_abm.cpp
index cbfdd2e9f..03fc82ed4 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<std::string> contents;
+
+public:
+ LiquidFlowABM(ServerEnvironment *env, INodeDefManager *nodemgr)
+ {
+ std::set<content_t> liquids;
+ nodemgr->getIds("group:liquid", liquids);
+ for(std::set<content_t>::const_iterator k = liquids.begin(); k != liquids.end(); k++)
+ contents.insert(nodemgr->get(*k).liquid_alternative_flowing);
+
+ }
+ virtual std::set<std::string> 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 4a5c001d5..0d80dc173 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 4307f7610..7afe8df05 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -206,6 +206,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 74bea3dac..07af2676b 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -1614,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);
diff --git a/src/map.h b/src/map.h
index 1d25b636b..d356da2d1 100644
--- a/src/map.h
+++ b/src/map.h
@@ -302,6 +302,7 @@ public:
virtual void PrintInfo(std::ostream &out);
void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
+ void transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks);
/*
Node metadata
@@ -330,6 +331,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 d70764e0e..1c8c5c49c 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 5021718a3..2d00cf4ac 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -659,6 +659,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;
@@ -834,6 +835,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()
@@ -1150,9 +1153,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 907be485d..d7700791c 100644
--- a/src/server.h
+++ b/src/server.h
@@ -636,6 +636,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;