commit before some radicallish changes to water behavior
authorPerttu Ahola <celeron55@gmail.com>
Sat, 11 Dec 2010 16:11:03 +0000 (18:11 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 11 Dec 2010 16:11:03 +0000 (18:11 +0200)
15 files changed:
Makefile
src/exceptions.h
src/main.cpp
src/map.cpp
src/map.h
src/mapblock.cpp
src/mapblock.h
src/mapnode.h
src/serialization.h
src/server.cpp
src/server.h
src/test.cpp
src/utility.h
src/voxel.cpp
src/voxel.h

index 102a3dcdb3f6c032c7b91b37060c7a8da7aaee5c..2b4e8dda3e20441aeb3f3543e4370bf6815b4c87 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,9 +13,9 @@ JTHREADPATH = ../jthread/jthread-1.2.1
 CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src\r
 \r
 #CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe\r
-#CXXFLAGS = -O2 -ffast-math -Wall -g -pipe\r
+CXXFLAGS = -O2 -ffast-math -Wall -g -pipe\r
 #CXXFLAGS = -O1 -ffast-math -Wall -g\r
-CXXFLAGS = -Wall -g -O0\r
+#CXXFLAGS = -Wall -g -O0\r
 \r
 #CXXFLAGS = -O3 -ffast-math -Wall\r
 #CXXFLAGS = -O3 -ffast-math -Wall -g\r
index 80bdbeb361c7b15252dbd2d4716493606449a1a3..0f95bd07a9de1e42d37f53ec409f48003c09b4d8 100644 (file)
@@ -116,6 +116,14 @@ public:
        {}
 };
 
+class ProcessingLimitException : public BaseException
+{
+public:
+       ProcessingLimitException(const char *s):
+               BaseException(s)
+       {}
+};
+
 /*
        Some "old-style" interrupts:
 */
index 938eb14ef842a42af9aa02ea693b45552545b013..73ef37951730f73717723ac8a7154c874e5b3065 100644 (file)
@@ -173,9 +173,12 @@ TODO: Remove LazyMeshUpdater. It is not used as supposed.
 FIXME: Rats somehow go underground sometimes (you can see it in water)\r
        - Does their position get saved to a border value or something?\r
 \r
-TODO: MovingObject::move and Player::move are basically the same.\r
+SUGG: MovingObject::move and Player::move are basically the same.\r
       combine them.\r
 \r
+TODO: Transfer sign texts as metadata of block and not as data of\r
+      object\r
+\r
 Doing now:\r
 ======================================================================\r
 \r
index 050299bc9d0a7fbfda50636327c55598897fdb15..88cb0f3f75e339d45257518a67af65e2b256d1ab 100644 (file)
@@ -47,10 +47,10 @@ MapBlockPointerCache::~MapBlockPointerCache()
 {
        m_map->m_blockcachelock.cacheRemoved();
 
-       dstream<<"MapBlockPointerCache:"
+       /*dstream<<"MapBlockPointerCache:"
                        <<" from_cache_count="<<m_from_cache_count
                        <<" from_map_count="<<m_from_map_count
-                       <<std::endl;
+                       <<std::endl;*/
 }
 
 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
@@ -1050,6 +1050,8 @@ void Map::removeNodeAndUpdate(v3s16 p,
        // Node will be replaced with this
        u8 replace_material = MATERIAL_AIR;
        
+       // NOTE: Water is now managed elsewhere
+#if 0
        {
                /*
                        Find out with what material the node will be replaced.
@@ -1107,6 +1109,8 @@ void Map::removeNodeAndUpdate(v3s16 p,
                }
        }
 
+#endif
+
        /*
                If there is a node at top and it doesn't have sunlight,
                there will be no sunlight going down.
@@ -3143,3 +3147,144 @@ void ClientMap::PrintInfo(std::ostream &out)
 }
 
 
+/*
+       MapVoxelManipulator
+*/
+
+MapVoxelManipulator::MapVoxelManipulator(Map *map)
+{
+       m_map = map;
+}
+
+MapVoxelManipulator::~MapVoxelManipulator()
+{
+       dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
+                       <<std::endl;
+}
+
+void MapVoxelManipulator::emerge(VoxelArea a)
+{
+       TimeTaker timer1("emerge", g_device, &emerge_time);
+
+       // Units of these are MapBlocks
+       v3s16 p_min = getNodeBlockPos(a.MinEdge);
+       v3s16 p_max = getNodeBlockPos(a.MaxEdge);
+
+       VoxelArea block_area_nodes
+                       (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
+
+       addArea(block_area_nodes);
+
+       for(s32 z=p_min.Z; z<=p_max.Z; z++)
+       for(s32 y=p_min.Y; y<=p_max.Y; y++)
+       for(s32 x=p_min.X; x<=p_max.X; x++)
+       {
+               v3s16 p(x,y,z);
+               core::map<v3s16, bool>::Node *n;
+               n = m_loaded_blocks.find(p);
+               if(n != NULL)
+                       continue;
+               
+               bool block_data_inexistent = false;
+               try
+               {
+                       TimeTaker timer1("emerge load", g_device, &emerge_load_time);
+
+                       dstream<<"Loading block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                                       <<std::endl;
+                       
+                       MapBlock *block = m_map->getBlockNoCreate(p);
+                       if(block->isDummy())
+                               block_data_inexistent = true;
+                       else
+                               block->copyTo(*this);
+               }
+               catch(InvalidPositionException &e)
+               {
+                       block_data_inexistent = true;
+               }
+
+               if(block_data_inexistent)
+               {
+                       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++)
+                       for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
+                       {
+                               s32 i = m_area.index(a.MinEdge.X,y,z);
+                               memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
+                       }
+               }
+
+               m_loaded_blocks.insert(p, true);
+       }
+
+       //dstream<<"emerge done"<<std::endl;
+}
+
+/*
+       TODO: Add an option to only update eg. water and air nodes.
+             This will make it interfere less with important stuff if
+                 run on background.
+*/
+void MapVoxelManipulator::blitBack
+               (core::map<v3s16, MapBlock*> & modified_blocks)
+{
+       TimeTaker timer1("blitBack", g_device);
+       
+       /*
+               Initialize block cache
+       */
+       v3s16 blockpos_last;
+       MapBlock *block = NULL;
+       bool block_checked_in_modified = false;
+
+       for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
+       for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
+       for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
+       {
+               v3s16 p(x,y,z);
+
+               u8 f = m_flags[m_area.index(p)];
+               if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
+                       continue;
+
+               MapNode &n = m_data[m_area.index(p)];
+                       
+               v3s16 blockpos = getNodeBlockPos(p);
+               
+               try
+               {
+                       // Get block
+                       if(block == NULL || blockpos != blockpos_last){
+                               block = m_map->getBlockNoCreate(blockpos);
+                               blockpos_last = blockpos;
+                               block_checked_in_modified = false;
+                       }
+                       
+                       // Calculate relative position in block
+                       v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
+
+                       // Don't continue if nothing has changed here
+                       if(block->getNode(relpos) == n)
+                               continue;
+
+                       //m_map->setNode(m_area.MinEdge + p, n);
+                       block->setNode(relpos, n);
+                       
+                       /*
+                               Make sure block is in modified_blocks
+                       */
+                       if(block_checked_in_modified == false)
+                       {
+                               modified_blocks[blockpos] = block;
+                               block_checked_in_modified = true;
+                       }
+               }
+               catch(InvalidPositionException &e)
+               {
+               }
+       }
+}
+
+//END
index 7f791ffad8a2490c1c096437831c6c4e20df3be7..62d1f8aee838ca2a8b28f966892639ecb9046e22 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock.h"
 #include "mapsector.h"
 #include "constants.h"
+#include "voxel.h"
 
 class Map;
 
@@ -49,6 +50,7 @@ class Map;
        NOTE: This doesn't really make anything more efficient
        NOTE: Use VoxelManipulator, if possible
        TODO: Get rid of this?
+       NOTE: CONFIRMED: THIS CACHE DOESN'T MAKE ANYTHING ANY FASTER
 */
 class MapBlockPointerCache : public NodeContainer
 {
@@ -121,7 +123,7 @@ public:
 
        void cacheCreated()
        {
-               dstream<<"cacheCreated() begin"<<std::endl;
+               //dstream<<"cacheCreated() begin"<<std::endl;
                JMutexAutoLock waitcachelock(m_waitcache_mutex);
                JMutexAutoLock countlock(m_count_mutex);
 
@@ -131,12 +133,12 @@ public:
                        
                m_count++;
 
-               dstream<<"cacheCreated() end"<<std::endl;
+               //dstream<<"cacheCreated() end"<<std::endl;
        }
 
        void cacheRemoved()
        {
-               dstream<<"cacheRemoved() begin"<<std::endl;
+               //dstream<<"cacheRemoved() begin"<<std::endl;
                JMutexAutoLock countlock(m_count_mutex);
 
                assert(m_count > 0);
@@ -147,7 +149,7 @@ public:
                if(m_count == 0)
                        m_cache_mutex.Unlock();
 
-               dstream<<"cacheRemoved() end"<<std::endl;
+               //dstream<<"cacheRemoved() end"<<std::endl;
        }
 
        /*
@@ -589,5 +591,29 @@ private:
        JMutex mesh_mutex;
 };
 
+class MapVoxelManipulator : public VoxelManipulator
+{
+public:
+       MapVoxelManipulator(Map *map);
+       virtual ~MapVoxelManipulator();
+       
+       virtual void clear()
+       {
+               VoxelManipulator::clear();
+               m_loaded_blocks.clear();
+       }
+
+       virtual void emerge(VoxelArea a);
+
+       void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
+
+private:
+       Map *m_map;
+       // bool is dummy value
+       // SUGG: How 'bout an another VoxelManipulator for storing the
+       //       information about which block is loaded?
+       core::map<v3s16, bool> m_loaded_blocks;
+};
+
 #endif
 
index a4da657d1ede796156797e2eb315a52bb42ffc8a..d2c3232910486771a87e3fd53b1d39ca9e837464 100644 (file)
@@ -696,6 +696,15 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
        return block_below_is_valid;
 }
 
+void MapBlock::copyTo(VoxelManipulator &dst)
+{
+       v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
+       VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
+       
+       dst.copyFrom(data, data_area, v3s16(0,0,0),
+                       getPosRelative(), data_size);
+}
+
 /*
        Serialization
 */
@@ -755,6 +764,17 @@ void MapBlock::serialize(std::ostream &os, u8 version)
                        paramdata[i] = data[i].param;
                }
                compress(paramdata, os, version);
+               
+               if(version >= 10)
+               {
+                       // Get and compress pressure
+                       SharedBuffer<u8> pressuredata(nodecount);
+                       for(u32 i=0; i<nodecount; i++)
+                       {
+                               pressuredata[i] = data[i].pressure;
+                       }
+                       compress(pressuredata, os, version);
+               }
        }
 }
 
@@ -819,6 +839,21 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
                                data[i].param = s[i];
                        }
                }
+       
+               if(version >= 10)
+               {
+                       // Uncompress and set pressure data
+                       std::ostringstream os(std::ios_base::binary);
+                       decompress(is, os, version);
+                       std::string s = os.str();
+                       if(s.size() != nodecount)
+                               throw SerializationError
+                                               ("MapBlock::deSerialize: invalid format");
+                       for(u32 i=0; i<s.size(); i++)
+                       {
+                               data[i].pressure = s[i];
+                       }
+               }
        }
 }
 
index 2c04343de31fd6ab98233a9dbca7bcffd613e9a8..c18bbb2b4ebd7eac0b47d25b93e0a11aae0f6b3e 100644 (file)
@@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serialization.h"
 #include "constants.h"
 #include "mapblockobject.h"
+#include "voxel.h"
 
 #define MAP_BLOCKSIZE 16
 
@@ -69,31 +70,6 @@ public:
 
 class MapBlock : public NodeContainer
 {
-private:
-
-       NodeContainer *m_parent;
-       // Position in blocks on parent
-       v3s16 m_pos;
-       /*
-               If NULL, block is a dummy block.
-               Dummy blocks are used for caching not-found-on-disk blocks.
-       */
-       MapNode * data;
-       /*
-               - On the client, this is used for checking whether to
-                 recalculate the face cache. (Is it anymore?)
-               - On the server, this is used for telling whether the
-                 block has been changed from the one on disk.
-       */
-       bool changed;
-       /*
-               Used for some initial lighting stuff.
-               At least /has been/ used. 8)
-       */
-       bool is_underground;
-       
-       MapBlockObjectList m_objects;
-       
 public:
 
        /*
@@ -333,10 +309,12 @@ public:
 
        bool propagateSunlight(core::map<v3s16, bool> & light_sources);
        
-       // Doesn't write version by itself
-       void serialize(std::ostream &os, u8 version);
+       // Copies data to VoxelManipulator to getPosRelative()
+       void copyTo(VoxelManipulator &dst);
 
-       void deSerialize(std::istream &is, u8 version);
+       /*
+               Object stuff
+       */
        
        void serializeObjects(std::ostream &os, u8 version)
        {
@@ -403,6 +381,15 @@ public:
                return m_objects.getCount();
        }
 
+       /*
+               Serialization
+       */
+       
+       // Doesn't write version by itself
+       void serialize(std::ostream &os, u8 version);
+
+       void deSerialize(std::istream &is, u8 version);
+
 private:
 
        /*
@@ -420,6 +407,31 @@ private:
        {
                return getNodeRef(p.X, p.Y, p.Z);
        }
+
+       
+       NodeContainer *m_parent;
+       // Position in blocks on parent
+       v3s16 m_pos;
+       /*
+               If NULL, block is a dummy block.
+               Dummy blocks are used for caching not-found-on-disk blocks.
+       */
+       MapNode * data;
+       /*
+               - On the client, this is used for checking whether to
+                 recalculate the face cache. (Is it anymore?)
+               - On the server, this is used for telling whether the
+                 block has been changed from the one on disk.
+       */
+       bool changed;
+       /*
+               Used for some initial lighting stuff.
+               At least /has been/ used. 8)
+       */
+       bool is_underground;
+       
+       MapBlockObjectList m_objects;
+       
 };
 
 inline bool blockpos_over_limit(v3s16 p)
index 02abe4e520dea1be3400722a6db22c0760768b50..7502c42d7d3b9a4dc4572f5eeed52847656d65bb 100644 (file)
@@ -205,10 +205,11 @@ struct MapNode
                *this = n;
        }
        
-       MapNode(u8 data=MATERIAL_AIR, u8 a_param=0)
+       MapNode(u8 data=MATERIAL_AIR, u8 a_param=0, u8 a_pressure=0)
        {
                d = data;
                param = a_param;
+               pressure = a_pressure;
        }
 
        bool operator==(const MapNode &other)
@@ -261,6 +262,11 @@ struct MapNode
                param = a_light;
        }
 
+       /*
+               These serialization functions are used when informing client
+               of a single node add
+       */
+
        static u32 serializedLength(u8 version)
        {
                if(!ser_ver_supported(version))
@@ -268,8 +274,10 @@ struct MapNode
                        
                if(version == 0)
                        return 1;
-               else
+               else if(version <= 9)
                        return 2;
+               else
+                       return 3;
        }
        void serialize(u8 *dest, u8 version)
        {
@@ -280,10 +288,16 @@ struct MapNode
                {
                        dest[0] = d;
                }
+               else if(version <= 9)
+               {
+                       dest[0] = d;
+                       dest[1] = param;
+               }
                else
                {
                        dest[0] = d;
                        dest[1] = param;
+                       dest[2] = pressure;
                }
        }
        void deSerialize(u8 *source, u8 version)
@@ -304,10 +318,16 @@ struct MapNode
                        else
                                param = source[1];
                }
+               else if(version <= 9)
+               {
+                       d = source[0];
+                       param = source[1];
+               }
                else
                {
                        d = source[0];
                        param = source[1];
+                       pressure = source[2];
                }
        }
 };
index 6e18497cf68492bf8dce40f4d2f12513de877eb0..8d3c5fee2f9dfa809646f3e7b0e157628bc522c0 100644 (file)
@@ -46,11 +46,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        7: block compression switched on again
        8: (dev) server-initiated block transfers and all kinds of stuff
        9: (dev) block objects
+       10: (dev) water pressure
 */
 // This represents an uninitialized or invalid format
 #define SER_FMT_VER_INVALID 255
 // Highest supported serialization version
-#define SER_FMT_VER_HIGHEST 9
+#define SER_FMT_VER_HIGHEST 10
 // Lowest supported serialization version
 #define SER_FMT_VER_LOWEST 2
 
index f8248acb4f594048e3862d8bec11a7555669e5da..8bcfe5216d5f7d0882013e726470c817a2243f5b 100644 (file)
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "jmutexautolock.h"
 #include "main.h"
 #include "constants.h"
+#include "voxel.h"
 
 void * ServerThread::Thread()
 {
@@ -990,6 +991,76 @@ void Server::AsyncRunStep()
        /*
                Do background stuff
        */
+
+       /*
+               Flow water
+       */
+       {
+               static float counter = 0.0;
+               counter += dtime;
+               if(counter >= 1.0)
+               {
+               
+               counter = 0.0;
+
+               core::map<v3s16, MapBlock*> modified_blocks;
+
+               {
+
+                       JMutexAutoLock lock(m_env_mutex);
+                       
+                       MapVoxelManipulator v(&m_env.getMap());
+                       
+                       /*try{
+                               v.flowWater(m_flow_active_nodes, 0, false, 20);
+                               //v.flowWater(p_under, 0, true, 100);
+                       }
+                       catch(ProcessingLimitException &e)
+                       {
+                               dstream<<"Processing limit reached"<<std::endl;
+                       }*/
+
+                       v.flowWater(m_flow_active_nodes, 0, false, 20);
+
+                       v.blitBack(modified_blocks);
+
+                       ServerMap &map = ((ServerMap&)m_env.getMap());
+                       
+                       // Update lighting
+                       core::map<v3s16, MapBlock*> lighting_modified_blocks;
+                       map.updateLighting(modified_blocks, lighting_modified_blocks);
+                       
+                       // Add blocks modified by lighting to modified_blocks
+                       for(core::map<v3s16, MapBlock*>::Iterator
+                                       i = lighting_modified_blocks.getIterator();
+                                       i.atEnd() == false; i++)
+                       {
+                               MapBlock *block = i.getNode()->getValue();
+                               modified_blocks.insert(block->getPos(), block);
+                       }
+               }
+
+               /*
+                       Set the modified blocks unsent for all the clients
+               */
+               
+               JMutexAutoLock lock2(m_con_mutex);
+
+               for(core::map<u16, RemoteClient*>::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);
+                       }
+               }
+
+               }
+       }
        
        // Periodically print some info
        {
@@ -1458,6 +1529,31 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                (this takes some time so it is done after the quick stuff)
                        */
                        m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
+                       
+                       /*
+                               Update water
+                       */
+                       
+                       // Update water pressure around modification
+                       // This also adds it to m_flow_active_nodes if appropriate
+
+                       MapVoxelManipulator v(&m_env.getMap());
+                       
+                       VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
+
+                       try
+                       {
+                               v.updateAreaWaterPressure(area, m_flow_active_nodes);
+                       }
+                       catch(ProcessingLimitException &e)
+                       {
+                               dstream<<"Processing limit reached"<<std::endl;
+                       }
+                       
+                       v.blitBack(modified_blocks);
+                       
+                       // Add the node to m_flow_active_nodes.
+                       //m_flow_active_nodes[p_under] = 1;
 
                } // button == 0
                /*
index a48f88805bb8b3b91d34391f176c6550e716900e..82e9136b5a0e06fbeda42cc5b19805f78f6f4c5a 100644 (file)
@@ -469,6 +469,9 @@ private:
 
        BlockEmergeQueue m_emerge_queue;
        
+       // Nodes that are destinations of flowing liquid at the moment
+       core::map<v3s16, u8> m_flow_active_nodes;
+       
        friend class EmergeThread;
        friend class RemoteClient;
 };
index 6b285e3a46475456b11f8c7d88315c22735a3e48..ebefb8e32fbc3f93fc133182d1560da23dfc0e58 100644 (file)
@@ -148,10 +148,45 @@ struct TestVoxelManipulator
 {
        void Run()
        {
+               /*
+                       VoxelArea
+               */
+
                VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1));
                assert(a.index(0,0,0) == 1*3*3 + 1*3 + 1);
                assert(a.index(-1,-1,-1) == 0);
+               
+               VoxelArea c(v3s16(-2,-2,-2), v3s16(2,2,2));
+               // An area that is 1 bigger in x+ and z-
+               VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2));
+               
+               core::list<VoxelArea> aa;
+               d.diff(c, aa);
+               
+               // Correct results
+               core::array<VoxelArea> results;
+               results.push_back(VoxelArea(v3s16(-2,-2,-3),v3s16(3,2,-3)));
+               results.push_back(VoxelArea(v3s16(3,-2,-2),v3s16(3,2,2)));
+
+               assert(aa.size() == results.size());
+               
+               dstream<<"Result of diff:"<<std::endl;
+               for(core::list<VoxelArea>::Iterator
+                               i = aa.begin(); i != aa.end(); i++)
+               {
+                       i->print(dstream);
+                       dstream<<std::endl;
+                       
+                       s32 j = results.linear_search(*i);
+                       assert(j != -1);
+                       results.erase(j, 1);
+               }
+
 
+               /*
+                       VoxelManipulator
+               */
+               
                VoxelManipulator v;
 
                v.print(dstream);
@@ -186,18 +221,19 @@ struct TestVoxelManipulator
                v.clear();
 
                const char *content =
-                       "#...######"
-                       "#...##..##"
-                       "#........ "
-                       "##########"
-
-                       "#...######"
-                       "#...##..##"
-                       "#........ "
-                       "##########"
+                       "#...######  "
+                       "#...##..##  "
+                       "#........ .."
+                       "############"
+
+                       "#...######  "
+                       "#...##..##  "
+                       "#........ "
+                       "############"
                ;
 
-               v3s16 size(10, 4, 2);
+               v3s16 size(12, 4, 2);
+               VoxelArea area(v3s16(0,0,0), size-v3s16(1,1,1));
                
                const char *p = content;
                for(s16 z=0; z<size.Z; z++)
@@ -205,7 +241,7 @@ struct TestVoxelManipulator
                for(s16 x=0; x<size.X; x++)
                {
                        MapNode n;
-                       n.pressure = size.Y - y;
+                       //n.pressure = size.Y - y;
                        if(*p == '#')
                                n.d = MATERIAL_STONE;
                        else if(*p == '.')
@@ -218,7 +254,24 @@ struct TestVoxelManipulator
                        p++;
                }
 
-               v.print(dstream);
+               v.print(dstream, VOXELPRINT_WATERPRESSURE);
+               
+               core::map<v3s16, u8> active_nodes;
+               v.updateAreaWaterPressure(area, active_nodes);
+
+               v.print(dstream, VOXELPRINT_WATERPRESSURE);
+               
+               s16 highest_y = -32768;
+               assert(v.getWaterPressure(v3s16(7, 1, 1), highest_y, 0) == -1);
+               assert(highest_y == 3);
+               
+               active_nodes.clear();
+               active_nodes[v3s16(9,1,0)] = 1;
+               //v.flowWater(active_nodes, 0, false);
+               v.flowWater(active_nodes, 0, true);
+               
+               dstream<<"Final result of flowWater:"<<std::endl;
+               v.print(dstream, VOXELPRINT_WATERPRESSURE);
                
                //assert(0);
        }
index 93fd9d4a7fe443ca38f04b57216356b6ddf95369..c9b13546c8f8d455fd9e6f2f117fd74b7e3b8a84 100644 (file)
@@ -394,12 +394,18 @@ private:
 class TimeTaker
 {
 public:
-       TimeTaker(const char *name, IrrlichtDevice *dev)
+       TimeTaker(const char *name, IrrlichtDevice *dev, u32 *result=NULL)
        {
                m_name = name;
                m_dev = dev;
-               m_time1 = m_dev->getTimer()->getRealTime();
+               m_result = result;
                m_running = true;
+               if(dev == NULL)
+               {
+                       m_time1 = 0;
+                       return;
+               }
+               m_time1 = m_dev->getTimer()->getRealTime();
        }
        ~TimeTaker()
        {
@@ -409,10 +415,24 @@ public:
        {
                if(m_running)
                {
+                       if(m_dev == NULL)
+                       {
+                               /*if(quiet == false)
+                                       std::cout<<"Couldn't measure time for "<<m_name
+                                                       <<": dev==NULL"<<std::endl;*/
+                               return 0;
+                       }
                        u32 time2 = m_dev->getTimer()->getRealTime();
                        u32 dtime = time2 - m_time1;
-                       if(quiet == false)
-                               std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
+                       if(m_result != NULL)
+                       {
+                               (*m_result) += dtime;
+                       }
+                       else
+                       {
+                               if(quiet == false)
+                                       std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
+                       }
                        m_running = false;
                        return dtime;
                }
@@ -423,6 +443,7 @@ private:
        IrrlichtDevice *m_dev;
        u32 m_time1;
        bool m_running;
+       u32 *m_result;
 };
 
 // Calculates the borders of a "d-radius" cube
index fe176a27a7d55d0998432dff54af85f3f07bd92d..15624feed5950cc1e0ba0c1244df0fa1b936e427 100644 (file)
@@ -20,6 +20,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "voxel.h"
 #include "map.h"
 
+// For TimeTaker
+#include "main.h"
+#include "utility.h"
+
+/*
+       Debug stuff
+*/
+u32 addarea_time = 0;
+u32 emerge_time = 0;
+u32 emerge_load_time = 0;
+u32 clearflag_time = 0;
+//u32 getwaterpressure_time = 0;
+//u32 spreadwaterpressure_time = 0;
+u32 updateareawaterpressure_time = 0;
+u32 flowwater_pre_time = 0;
+
+
 VoxelManipulator::VoxelManipulator():
        m_data(NULL),
        m_flags(NULL)
@@ -47,7 +64,7 @@ void VoxelManipulator::clear()
        m_flags = NULL;
 }
 
-void VoxelManipulator::print(std::ostream &o)
+void VoxelManipulator::print(std::ostream &o, VoxelPrintMode mode)
 {
        v3s16 em = m_area.getExtent();
        v3s16 of = m_area.MinEdge;
@@ -78,8 +95,29 @@ void VoxelManipulator::print(std::ostream &o)
                                {
                                        c = 'X';
                                        u8 m = m_data[m_area.index(x,y,z)].d;
-                                       if(m <= 9)
-                                               c = m + '0';
+                                       u8 pr = m_data[m_area.index(x,y,z)].pressure;
+                                       if(mode == VOXELPRINT_MATERIAL)
+                                       {
+                                               if(m <= 9)
+                                                       c = m + '0';
+                                       }
+                                       else if(mode == VOXELPRINT_WATERPRESSURE)
+                                       {
+                                               if(m == MATERIAL_WATER)
+                                               {
+                                                       c = 'w';
+                                                       if(pr <= 9)
+                                                               c = pr + '0';
+                                               }
+                                               else if(m == MATERIAL_AIR)
+                                               {
+                                                       c = ' ';
+                                               }
+                                               else
+                                               {
+                                                       c = '#';
+                                               }
+                                       }
                                }
                                o<<c;
                        }
@@ -99,6 +137,8 @@ void VoxelManipulator::addArea(VoxelArea area)
        if(m_area.contains(area))
                return;
        
+       TimeTaker timer("addArea", g_device, &addarea_time);
+
        // Calculate new area
        VoxelArea new_area;
        // New area is the requested area if m_area has zero volume
@@ -163,6 +203,21 @@ void VoxelManipulator::addArea(VoxelArea area)
                delete[] old_data;
        if(old_flags)
                delete[] old_flags;
+
+       //dstream<<"addArea done"<<std::endl;
+}
+
+void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area,
+               v3s16 from_pos, v3s16 to_pos, v3s16 size)
+{
+       for(s16 z=0; z<size.Z; z++)
+       for(s16 y=0; y<size.Y; y++)
+       {
+               s32 i_src = src_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z);
+               s32 i_local = m_area.index(to_pos.X, to_pos.Y+y, to_pos.Z+z);
+               memcpy(&m_data[i_local], &src[i_src], size.X*sizeof(MapNode));
+               memset(&m_flags[i_local], 0, size.X);
+       }
 }
 
 void VoxelManipulator::interpolate(VoxelArea area)
@@ -230,7 +285,318 @@ void VoxelManipulator::interpolate(VoxelArea area)
        }
 }
 
-void VoxelManipulator::flowWater(v3s16 removed_pos)
+
+void VoxelManipulator::clearFlag(u8 flags)
+{
+       // 0-1ms on moderate area
+       TimeTaker timer("clearFlag", g_device, &clearflag_time);
+
+       v3s16 s = m_area.getExtent();
+
+       /*dstream<<"clearFlag clearing area of size "
+                       <<""<<s.X<<"x"<<s.Y<<"x"<<s.Z<<""
+                       <<std::endl;*/
+
+       //s32 count = 0;
+
+       /*for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
+       for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
+       for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
+       {
+               u8 f = m_flags[m_area.index(x,y,z)];
+               m_flags[m_area.index(x,y,z)] &= ~flags;
+               if(m_flags[m_area.index(x,y,z)] != f)
+                       count++;
+       }*/
+
+       s32 volume = m_area.getVolume();
+       for(s32 i=0; i<volume; i++)
+       {
+               m_flags[i] &= ~flags;
+       }
+
+       /*s32 volume = m_area.getVolume();
+       for(s32 i=0; i<volume; i++)
+       {
+               u8 f = m_flags[i];
+               m_flags[i] &= ~flags;
+               if(m_flags[i] != f)
+                       count++;
+       }
+
+       dstream<<"clearFlag changed "<<count<<" flags out of "
+                       <<volume<<" nodes"<<std::endl;*/
+}
+
+int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
+{
+       m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED2;
+
+       if(p.Y > highest_y)
+               highest_y = p.Y;
+       
+       recur_count++;
+       if(recur_count > 30)
+               throw ProcessingLimitException
+                               ("getWaterPressure recur_count limit reached");
+
+       v3s16 dirs[6] = {
+               v3s16(0,1,0), // top
+               v3s16(-1,0,0), // left
+               v3s16(1,0,0), // right
+               v3s16(0,0,-1), // front
+               v3s16(0,0,1), // back
+               v3s16(0,-1,0), // bottom
+       };
+
+       // Load neighboring nodes
+       // TODO: A bigger area would be better
+       emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
+
+       s32 i;
+       for(i=0; i<6; i++)
+       {
+               v3s16 p2 = p + dirs[i];
+               u8 f = m_flags[m_area.index(p2)];
+               // Ignore inexistent or checked nodes
+               if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED2))
+                       continue;
+               MapNode &n = m_data[m_area.index(p2)];
+               // Ignore non-liquid nodes
+               if(material_liquid(n.d) == false)
+                       continue;
+
+               int pr;
+               
+               // If at surface
+               /*if(n.pressure == 1)
+               {
+                       pr = 1;
+               }
+               // Otherwise recurse more
+               else*/
+               {
+                       pr = getWaterPressure(p2, highest_y, recur_count);
+                       if(pr == -1)
+                               continue;
+               }
+
+               // If block is at top, pressure here is one higher
+               if(i == 0)
+               {
+                       if(pr < 255)
+                               pr++;
+               }
+               // If block is at bottom, pressure here is one lower
+               else if(i == 5)
+               {
+                       if(pr > 1)
+                               pr--;
+               }
+               
+               // Node is on the pressure route
+               m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED4;
+
+               // Got pressure
+               return pr;
+       }
+       
+       // Nothing useful found
+       return -1;
+}
+
+void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
+               VoxelArea request_area,
+               core::map<v3s16, u8> &active_nodes,
+               int recur_count)
+{
+       recur_count++;
+       if(recur_count > 10000)
+               throw ProcessingLimitException
+                               ("spreadWaterPressure recur_count limit reached");
+
+       m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3;
+       m_data[m_area.index(p)].pressure = pr;
+
+       v3s16 dirs[6] = {
+               v3s16(0,1,0), // top
+               v3s16(-1,0,0), // left
+               v3s16(1,0,0), // right
+               v3s16(0,0,-1), // front
+               v3s16(0,0,1), // back
+               v3s16(0,-1,0), // bottom
+       };
+
+       // Load neighboring nodes
+       emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
+
+       s32 i;
+       for(i=0; i<6; i++)
+       {
+               v3s16 p2 = p + dirs[i];
+               
+               u8 f = m_flags[m_area.index(p2)];
+
+               // Ignore inexistent and checked nodes
+               if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
+                       continue;
+
+               MapNode &n = m_data[m_area.index(p2)];
+               
+               /*
+                       If material is air:
+                               add to active_nodes if there is flow-causing pressure.
+                       NOTE: Do not remove anything from there. We cannot know
+                             here if some other neighbor of it causes flow.
+               */
+               if(n.d == MATERIAL_AIR)
+               {
+                       bool pressure_causes_flow = false;
+                       // If block is at top
+                       if(i == 0)
+                       {
+                               if(pr >= 3)
+                                       pressure_causes_flow = true;
+                       }
+                       // If block is at bottom
+                       else if(i == 5)
+                       {
+                               pressure_causes_flow = true;
+                       }
+                       // If block is at side
+                       else
+                       {
+                               if(pr >= 2)
+                                       pressure_causes_flow = true;
+                       }
+                       
+                       if(pressure_causes_flow)
+                       {
+                               active_nodes[p2] = 1;
+                       }
+
+                       continue;
+               }
+
+               // Ignore non-liquid nodes
+               if(material_liquid(n.d) == false)
+                       continue;
+
+               int pr2 = pr;
+               // If block is at top, pressure there is lower
+               if(i == 0)
+               {
+                       if(pr2 > 0)
+                               pr2--;
+               }
+               // If block is at bottom, pressure there is higher
+               else if(i == 5)
+               {
+                       if(pr2 < 255)
+                               pr2++;
+               }
+               
+               // Ignore if correct pressure is already set and is not on
+               // request_area
+               if(n.pressure == pr2 && request_area.contains(p2) == false)
+                       continue;
+
+               spreadWaterPressure(p2, pr2, request_area, active_nodes, recur_count);
+       }
+}
+
+void VoxelManipulator::updateAreaWaterPressure(VoxelArea a,
+               core::map<v3s16, u8> &active_nodes,
+               bool checked3_is_clear)
+{
+       TimeTaker timer("updateAreaWaterPressure", g_device,
+                       &updateareawaterpressure_time);
+
+       emerge(a);
+       
+       bool checked2_clear = false;
+       
+       if(checked3_is_clear == false)
+       {
+               //clearFlag(VOXELFLAG_CHECKED3);
+
+               clearFlag(VOXELFLAG_CHECKED3 | VOXELFLAG_CHECKED2);
+               checked2_clear = true;
+       }
+       
+
+       for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
+       for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
+       for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
+       {
+               v3s16 p(x,y,z);
+
+               u8 f = m_flags[m_area.index(p)];
+               // Ignore inexistent or checked nodes
+               if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED3))
+                       continue;
+               MapNode &n = m_data[m_area.index(p)];
+               // Ignore non-liquid nodes
+               if(material_liquid(n.d) == false)
+                       continue;
+               
+               if(checked2_clear == false)
+               {
+                       clearFlag(VOXELFLAG_CHECKED2);
+                       checked2_clear = true;
+               }
+
+               checked2_clear = false;
+
+               s16 highest_y = -32768;
+               int recur_count = 0;
+               int pr = -1;
+
+               try
+               {
+                       // 0-1ms @ recur_count <= 100
+                       //TimeTaker timer("getWaterPressure", g_device);
+                       pr = getWaterPressure(p, highest_y, recur_count);
+               }
+               catch(ProcessingLimitException &e)
+               {
+                       //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
+               }
+
+               if(pr == -1)
+               {
+                       assert(highest_y != -32768);
+
+                       pr = highest_y - p.Y + 1;
+                       if(pr > 255)
+                               pr = 255;
+
+                       /*dstream<<"WARNING: Pressure at ("
+                                       <<p.X<<","<<p.Y<<","<<p.Z<<")"
+                                       <<" = "<<pr
+                                       //<<" and highest_y == -32768"
+                                       <<std::endl;
+                       assert(highest_y != -32768);
+                       continue;*/
+               }
+               
+               try
+               {
+                       // 0ms
+                       //TimeTaker timer("spreadWaterPressure", g_device);
+                       spreadWaterPressure(p, pr, a, active_nodes, 0);
+               }
+               catch(ProcessingLimitException &e)
+               {
+                       //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
+               }
+       }
+}
+
+bool VoxelManipulator::flowWater(v3s16 removed_pos,
+               core::map<v3s16, u8> &active_nodes,
+               int recursion_depth, bool debugprint,
+               int *counter, int counterlimit)
 {
        v3s16 dirs[6] = {
                v3s16(0,1,0), // top
@@ -241,16 +607,43 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
                v3s16(0,-1,0), // bottom
        };
 
+       recursion_depth++;
+
        v3s16 p;
+       
+       // Randomize horizontal order
+       static s32 cs = 0;
+       if(cs < 3)
+               cs++;
+       else
+               cs = 0;
+       s16 s1 = (cs & 1) ? 1 : -1;
+       s16 s2 = (cs & 2) ? 1 : -1;
+       //dstream<<"s1="<<s1<<", s2="<<s2<<std::endl;
 
+       {
+       TimeTaker timer1("flowWater pre", g_device, &flowwater_pre_time);
+       
        // Load neighboring nodes
-       // TODO: A bigger area would be better
        emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)));
-
+       
+       // Ignore incorrect removed_pos
+       {
+               u8 f = m_flags[m_area.index(removed_pos)];
+               // Ignore inexistent or checked node
+               if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
+                       return false;
+               MapNode &n = m_data[m_area.index(removed_pos)];
+               // Water can move only to air
+               if(n.d != MATERIAL_AIR)
+                       return false;
+       }
+       
        s32 i;
        for(i=0; i<6; i++)
        {
-               p = removed_pos + dirs[i];
+               p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
+
                u8 f = m_flags[m_area.index(p)];
                // Inexistent or checked nodes can't move
                if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
@@ -280,30 +673,66 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
 
        // If there is nothing to move, return
        if(i==6)
-               return;
-       
+               return false;
+
        // Switch nodes at p and removed_pos
-       MapNode n = m_data[m_area.index(p)];
+       u8 m = m_data[m_area.index(p)].d;
        u8 f = m_flags[m_area.index(p)];
-       m_data[m_area.index(p)] = m_data[m_area.index(removed_pos)];
+       m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d;
        m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
-       m_data[m_area.index(removed_pos)] = n;
+       m_data[m_area.index(removed_pos)].d = m;
        m_flags[m_area.index(removed_pos)] = f;
 
-       // Mark p checked
-       m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED;
+       // Mark removed_pos checked
+       m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
+       // If block was dropped from surface, increase pressure
+       if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
+       {
+               m_data[m_area.index(removed_pos)].pressure = 2;
+       }
        
+       /*if(debugprint)
+       {
+               dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
+               print(dstream, VOXELPRINT_WATERPRESSURE);
+       }*/
+
        // Update pressure
-       //TODO
+       VoxelArea a;
+       a.addPoint(p - v3s16(1,1,1));
+       a.addPoint(p + v3s16(1,1,1));
+       a.addPoint(removed_pos - v3s16(1,1,1));
+       a.addPoint(removed_pos + v3s16(1,1,1));
+       updateAreaWaterPressure(a, active_nodes);
+       
+       /*if(debugprint)
+       {
+               dstream<<"VoxelManipulator::flowWater(): Pressure updated:"<<std::endl;
+               print(dstream, VOXELPRINT_WATERPRESSURE);
+               //std::cin.get();
+       }*/
+
+       if(debugprint)
+       {
+               dstream<<"VoxelManipulator::flowWater(): step done:"<<std::endl;
+               print(dstream, VOXELPRINT_WATERPRESSURE);
+               //std::cin.get();
+       }
+       
+       }//timer1
 
        // Flow water to the newly created empty position
-       flowWater(p);
+       flowWater(p, active_nodes, recursion_depth,
+                       debugprint, counter, counterlimit);
        
+find_again:
        // Try flowing water to empty positions around removed_pos.
        // They are checked in reverse order compared to the previous loop.
-       for(i=5; i>=0; i--)
+       for(s32 i=5; i>=0; i--)
        {
-               p = removed_pos + dirs[i];
+               //v3s16 p = removed_pos + dirs[i];
+               p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
+
                u8 f = m_flags[m_area.index(p)];
                // Water can't move to inexistent nodes
                if(f & VOXELFLAG_INEXISTENT)
@@ -312,102 +741,291 @@ void VoxelManipulator::flowWater(v3s16 removed_pos)
                // Water can only move to air
                if(n.d != MATERIAL_AIR)
                        continue;
-               flowWater(p);
+                       
+               // Flow water to node
+               bool moved =
+               flowWater(p, active_nodes, recursion_depth,
+                               debugprint, counter, counterlimit);
+               
+               if(moved)
+               {
+                       // Search again from all neighbors
+                       goto find_again;
+               }
        }
-}
 
-/*
-       MapVoxelManipulator
-*/
+       if(counter != NULL)
+       {
+               (*counter)++;
+               if((*counter) % 10 == 0)
+                       dstream<<"flowWater(): moved "<<(*counter)<<" nodes"
+                                       <<std::endl;
 
-MapVoxelManipulator::MapVoxelManipulator(Map *map)
-{
-       m_map = map;
+               if(counterlimit != -1 && (*counter) > counterlimit)
+               {
+                       dstream<<"Counter limit reached; returning"<<std::endl;
+                       throw ProcessingLimitException("flowWater counterlimit reached");
+               }
+       }
+       
+       return true;
 }
-
-void MapVoxelManipulator::emerge(VoxelArea a)
+bool VoxelManipulator::flowWater(v3s16 removed_pos,
+               core::map<v3s16, u8> &active_nodes,
+               int recursion_depth, bool debugprint,
+               int *counter, int counterlimit)
 {
-       v3s16 size = a.getExtent();
+       v3s16 dirs[6] = {
+               v3s16(0,1,0), // top
+               v3s16(-1,0,0), // left
+               v3s16(1,0,0), // right
+               v3s16(0,0,-1), // front
+               v3s16(0,0,1), // back
+               v3s16(0,-1,0), // bottom
+       };
 
-       addArea(a);
+       recursion_depth++;
+
+       v3s16 p;
+       
+       // Randomize horizontal order
+       static s32 cs = 0;
+       if(cs < 3)
+               cs++;
+       else
+               cs = 0;
+       s16 s1 = (cs & 1) ? 1 : -1;
+       s16 s2 = (cs & 2) ? 1 : -1;
+       //dstream<<"s1="<<s1<<", s2="<<s2<<std::endl;
 
-       for(s16 z=0; z<size.Z; z++)
-       for(s16 y=0; y<size.Y; y++)
-       for(s16 x=0; x<size.X; x++)
        {
-               v3s16 p(x,y,z);
-               s32 i = m_area.index(a.MinEdge + p);
-               // Don't touch nodes that have already been loaded
-               if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
+       TimeTaker timer1("flowWater pre", g_device, &flowwater_pre_time);
+       
+       // Load neighboring nodes
+       emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)));
+       
+       // Ignore incorrect removed_pos
+       {
+               u8 f = m_flags[m_area.index(removed_pos)];
+               // Ignore inexistent or checked node
+               if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
+                       return false;
+               MapNode &n = m_data[m_area.index(removed_pos)];
+               // Water can move only to air
+               if(n.d != MATERIAL_AIR)
+                       return false;
+       }
+       
+       s32 i;
+       for(i=0; i<6; i++)
+       {
+               p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
+
+               u8 f = m_flags[m_area.index(p)];
+               // Inexistent or checked nodes can't move
+               if(f & (VOXELFLAG_INEXISTENT | VOXELFLAG_CHECKED))
+                       continue;
+               MapNode &n = m_data[m_area.index(p)];
+               // Only liquid nodes can move
+               if(material_liquid(n.d) == false)
+                       continue;
+               // If block is at top, select it always
+               if(i == 0)
+               {
+                       break;
+               }
+               // If block is at bottom, select it if it has enough pressure
+               if(i == 5)
+               {
+                       if(n.pressure >= 3)
+                               break;
                        continue;
-               try{
-                       MapNode n = m_map->getNode(a.MinEdge + p);
-                       m_data[i] = n;
-                       m_flags[i] = 0;
                }
-               catch(InvalidPositionException &e)
+               // Else block is at some side. Select it if it has enough pressure
+               if(n.pressure >= 2)
                {
-                       m_flags[i] = VOXELFLAG_INEXISTENT;
+                       break;
                }
        }
-}
 
-void MapVoxelManipulator::blitBack
-               (core::map<v3s16, MapBlock*> & modified_blocks)
-{
-       /*
-               Initialize block cache
-       */
-       v3s16 blockpos_last;
-       MapBlock *block = NULL;
-       bool block_checked_in_modified = false;
+       // If there is nothing to move, return
+       if(i==6)
+               return false;
 
-       for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
-       for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
-       for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
+       // Switch nodes at p and removed_pos
+       u8 m = m_data[m_area.index(p)].d;
+       u8 f = m_flags[m_area.index(p)];
+       m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d;
+       m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
+       m_data[m_area.index(removed_pos)].d = m;
+       m_flags[m_area.index(removed_pos)] = f;
+
+       // Mark removed_pos checked
+       m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
+       // If block was dropped from surface, increase pressure
+       if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
        {
-               v3s16 p(x,y,z);
+               m_data[m_area.index(removed_pos)].pressure = 2;
+       }
+       
+       /*if(debugprint)
+       {
+               dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
+               print(dstream, VOXELPRINT_WATERPRESSURE);
+       }*/
+
+       // Update pressure
+       VoxelArea a;
+       a.addPoint(p - v3s16(1,1,1));
+       a.addPoint(p + v3s16(1,1,1));
+       a.addPoint(removed_pos - v3s16(1,1,1));
+       a.addPoint(removed_pos + v3s16(1,1,1));
+       updateAreaWaterPressure(a, active_nodes);
+       
+       /*if(debugprint)
+       {
+               dstream<<"VoxelManipulator::flowWater(): Pressure updated:"<<std::endl;
+               print(dstream, VOXELPRINT_WATERPRESSURE);
+               //std::cin.get();
+       }*/
+
+       if(debugprint)
+       {
+               dstream<<"VoxelManipulator::flowWater(): step done:"<<std::endl;
+               print(dstream, VOXELPRINT_WATERPRESSURE);
+               //std::cin.get();
+       }
+       
+       }//timer1
+
+       // Flow water to the newly created empty position
+       flowWater(p, active_nodes, recursion_depth,
+                       debugprint, counter, counterlimit);
+       
+find_again:
+       // Try flowing water to empty positions around removed_pos.
+       // They are checked in reverse order compared to the previous loop.
+       for(s32 i=5; i>=0; i--)
+       {
+               //v3s16 p = removed_pos + dirs[i];
+               p = removed_pos + v3s16(s1*dirs[i].X, dirs[i].Y, s2*dirs[i].Z);
 
                u8 f = m_flags[m_area.index(p)];
-               if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
+               // Water can't move to inexistent nodes
+               if(f & VOXELFLAG_INEXISTENT)
                        continue;
-
                MapNode &n = m_data[m_area.index(p)];
+               // Water can only move to air
+               if(n.d != MATERIAL_AIR)
+                       continue;
                        
-               v3s16 blockpos = getNodeBlockPos(p);
+               // Flow water to node
+               bool moved =
+               flowWater(p, active_nodes, recursion_depth,
+                               debugprint, counter, counterlimit);
                
-               try
+               if(moved)
                {
-                       // Get block
-                       if(block == NULL || blockpos != blockpos_last){
-                               block = m_map->getBlockNoCreate(blockpos);
-                               blockpos_last = blockpos;
-                               block_checked_in_modified = false;
-                       }
-                       
-                       // Calculate relative position in block
-                       v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
+                       // Search again from all neighbors
+                       goto find_again;
+               }
+       }
 
-                       // Don't continue if nothing has changed here
-                       if(block->getNode(relpos) == n)
-                               continue;
+       if(counter != NULL)
+       {
+               (*counter)++;
+               if((*counter) % 10 == 0)
+                       dstream<<"flowWater(): moved "<<(*counter)<<" nodes"
+                                       <<std::endl;
 
-                       //m_map->setNode(m_area.MinEdge + p, n);
-                       block->setNode(relpos, n);
-                       
-                       /*
-                               Make sure block is in modified_blocks
-                       */
-                       if(block_checked_in_modified == false)
-                       {
-                               modified_blocks[blockpos] = block;
-                               block_checked_in_modified = true;
-                       }
+               if(counterlimit != -1 && (*counter) > counterlimit)
+               {
+                       dstream<<"Counter limit reached; returning"<<std::endl;
+                       throw ProcessingLimitException("flowWater counterlimit reached");
                }
-               catch(InvalidPositionException &e)
+       }
+       
+       return true;
+}
+
+void VoxelManipulator::flowWater(
+               core::map<v3s16, u8> &active_nodes,
+               int recursion_depth, bool debugprint,
+               int counterlimit)
+{
+       addarea_time = 0;
+       emerge_time = 0;
+       emerge_load_time = 0;
+       clearflag_time = 0;
+       updateareawaterpressure_time = 0;
+       flowwater_pre_time = 0;
+
+       TimeTaker timer1("flowWater (active_nodes)", g_device);
+
+       dstream<<"active_nodes.size() = "<<active_nodes.size()<<std::endl;
+
+       int counter = 0;
+
+       try
+       {
+
+       // Flow water to active nodes
+       for(;;)
+       {
+               // Clear check flags
+               clearFlag(VOXELFLAG_CHECKED);
+               
+               if(active_nodes.size() == 0)
+                       break;
+
+               dstream<<"Selecting a new active_node"<<std::endl;
+
+#if 0
+               // Take first one
+               core::map<v3s16, u8>::Node
+                               *n = active_nodes.getIterator().getNode();
+#endif
+
+#if 1
+               // Take random one
+               s32 k = (s32)rand() % (s32)active_nodes.size();
+               //s32 k = 0;
+               core::map<v3s16, u8>::Iterator
+                               i = active_nodes.getIterator().getNode();
+               for(s32 j=0; j<k; j++)
                {
+                       i++;
                }
+               core::map<v3s16, u8>::Node *n = i.getNode();
+#endif
+
+               v3s16 p = n->getKey();
+               active_nodes.remove(p);
+               flowWater(p, active_nodes, recursion_depth,
+                               debugprint, &counter, counterlimit);
+       }
+
        }
+       catch(ProcessingLimitException &e)
+       {
+               //dstream<<"getWaterPressure ProcessingLimitException"<<std::endl;
+       }
+       
+       v3s16 e = m_area.getExtent();
+       s32 v = m_area.getVolume();
+       dstream<<"flowWater (active): moved "<<counter<<" nodes, "
+                       <<"area ended up as "
+                       <<e.X<<"x"<<e.Y<<"x"<<e.Z<<" = "<<v
+                       <<std::endl;
+       
+       dstream<<"addarea_time: "<<addarea_time
+                       <<", emerge_time: "<<emerge_time
+                       <<", emerge_load_time: "<<emerge_load_time
+                       <<", clearflag_time: "<<clearflag_time
+                       <<", flowwater_pre_time: "<<flowwater_pre_time
+                       <<", updateareawaterpressure_time: "<<updateareawaterpressure_time
+                       <<std::endl;
 }
 
+
 //END
index 3a4dacd4909b94d90b0a2dd51498e51b1290d37c..74c0a00e5dc5b0198ea9d1a7d09a89b4bc82eae3 100644 (file)
@@ -21,8 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define VOXEL_HEADER
 
 #include "common_irrlicht.h"
-#include "mapblock.h"
 #include <iostream>
+#include "debug.h"
+#include "mapnode.h"
 
 /*
        A fast voxel manipulator class
@@ -30,6 +31,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        Not thread-safe.
 */
 
+/*
+       Debug stuff
+*/
+extern u32 emerge_time;
+extern u32 emerge_load_time;
+
 /*
        This class resembles aabbox3d<s16> a lot, but has inclusive
        edges for saner handling of integer sizes
@@ -53,8 +60,18 @@ public:
                MaxEdge(p)
        {
        }
+       
+       /*
+               Modifying methods
+       */
+
        void addArea(VoxelArea &a)
        {
+               if(getExtent() == v3s16(0,0,0))
+               {
+                       *this = a;
+                       return;
+               }
                if(a.MinEdge.X < MinEdge.X) MinEdge.X = a.MinEdge.X;
                if(a.MinEdge.Y < MinEdge.Y) MinEdge.Y = a.MinEdge.Y;
                if(a.MinEdge.Z < MinEdge.Z) MinEdge.Z = a.MinEdge.Z;
@@ -64,6 +81,12 @@ public:
        }
        void addPoint(v3s16 p)
        {
+               if(getExtent() == v3s16(0,0,0))
+               {
+                       MinEdge = p;
+                       MaxEdge = p;
+                       return;
+               }
                if(p.X < MinEdge.X) MinEdge.X = p.X;
                if(p.Y < MinEdge.Y) MinEdge.Y = p.Y;
                if(p.Z < MinEdge.Z) MinEdge.Z = p.Z;
@@ -71,6 +94,30 @@ public:
                if(p.Y > MaxEdge.Y) MaxEdge.Y = p.Y;
                if(p.Z > MaxEdge.Z) MaxEdge.Z = p.Z;
        }
+       
+       // Pad with d nodes
+       void pad(v3s16 d)
+       {
+               MinEdge -= d;
+               MaxEdge += d;
+       }
+       
+       /*void operator+=(v3s16 off)
+       {
+               MinEdge += off;
+               MaxEdge += off;
+       }
+
+       void operator-=(v3s16 off)
+       {
+               MinEdge -= off;
+               MaxEdge -= off;
+       }*/
+
+       /*
+               const methods
+       */
+
        v3s16 getExtent() const
        {
                return MaxEdge - MinEdge + v3s16(1,1,1);
@@ -80,8 +127,13 @@ public:
                v3s16 e = getExtent();
                return (s32)e.X * (s32)e.Y * (s32)e.Z;
        }
-       bool contains(VoxelArea &a) const
+       bool contains(const VoxelArea &a) const
        {
+               // No area contains an empty area
+               // NOTE: Algorithms depend on this, so do not change.
+               if(a.getExtent() == v3s16(0,0,0))
+                       return false;
+
                return(
                        a.MinEdge.X >= MinEdge.X && a.MaxEdge.X <= MaxEdge.X &&
                        a.MinEdge.Y >= MinEdge.Y && a.MaxEdge.Y <= MaxEdge.Y &&
@@ -101,6 +153,95 @@ public:
                return (MinEdge == other.MinEdge
                                && MaxEdge == other.MaxEdge);
        }
+
+       VoxelArea operator+(v3s16 off) const
+       {
+               return VoxelArea(MinEdge+off, MaxEdge+off);
+       }
+
+       VoxelArea operator-(v3s16 off) const
+       {
+               return VoxelArea(MinEdge-off, MaxEdge-off);
+       }
+
+       /*
+               Returns 0-6 non-overlapping areas that can be added to
+               a to make up this area.
+
+               a: area inside *this
+       */
+       void diff(const VoxelArea &a, core::list<VoxelArea> &result)
+       {
+               /*
+                       This can result in a maximum of 6 areas
+               */
+
+               // If a is an empty area, return the current area as a whole
+               if(a.getExtent() == v3s16(0,0,0))
+               {
+                       VoxelArea b = *this;
+                       if(b.getVolume() != 0)
+                               result.push_back(b);
+                       return;
+               }
+
+               assert(contains(a));
+               
+               // Take back area, XY inclusive
+               {
+                       v3s16 min(MinEdge.X, MinEdge.Y, a.MaxEdge.Z+1);
+                       v3s16 max(MaxEdge.X, MaxEdge.Y, MaxEdge.Z);
+                       VoxelArea b(min, max);
+                       if(b.getVolume() != 0)
+                               result.push_back(b);
+               }
+
+               // Take front area, XY inclusive
+               {
+                       v3s16 min(MinEdge.X, MinEdge.Y, MinEdge.Z);
+                       v3s16 max(MaxEdge.X, MaxEdge.Y, a.MinEdge.Z-1);
+                       VoxelArea b(min, max);
+                       if(b.getVolume() != 0)
+                               result.push_back(b);
+               }
+
+               // Take top area, X inclusive
+               {
+                       v3s16 min(MinEdge.X, a.MaxEdge.Y+1, a.MinEdge.Z);
+                       v3s16 max(MaxEdge.X, MaxEdge.Y, a.MaxEdge.Z);
+                       VoxelArea b(min, max);
+                       if(b.getVolume() != 0)
+                               result.push_back(b);
+               }
+
+               // Take bottom area, X inclusive
+               {
+                       v3s16 min(MinEdge.X, MinEdge.Y, a.MinEdge.Z);
+                       v3s16 max(MaxEdge.X, a.MinEdge.Y-1, a.MaxEdge.Z);
+                       VoxelArea b(min, max);
+                       if(b.getVolume() != 0)
+                               result.push_back(b);
+               }
+
+               // Take left area, non-inclusive
+               {
+                       v3s16 min(MinEdge.X, a.MinEdge.Y, a.MinEdge.Z);
+                       v3s16 max(a.MinEdge.X-1, a.MaxEdge.Y, a.MaxEdge.Z);
+                       VoxelArea b(min, max);
+                       if(b.getVolume() != 0)
+                               result.push_back(b);
+               }
+
+               // Take right area, non-inclusive
+               {
+                       v3s16 min(a.MaxEdge.X+1, a.MinEdge.Y, a.MinEdge.Z);
+                       v3s16 max(MaxEdge.X, a.MaxEdge.Y, a.MaxEdge.Z);
+                       VoxelArea b(min, max);
+                       if(b.getVolume() != 0)
+                               result.push_back(b);
+               }
+
+       }
        
        /*
                Translates position from virtual coordinates to array index
@@ -120,13 +261,15 @@ public:
 
        void print(std::ostream &o) const
        {
+               v3s16 e = getExtent();
                o<<"("<<MinEdge.X
                 <<","<<MinEdge.Y
                 <<","<<MinEdge.Z
                 <<")("<<MaxEdge.X
                 <<","<<MaxEdge.Y
                 <<","<<MaxEdge.Z
-                <<")";
+                <<")"
+                <<"="<<e.X<<"x"<<e.Y<<"x"<<e.Z<<"="<<getVolume();
        }
 
        // Edges are inclusive
@@ -139,18 +282,35 @@ public:
 // Checked as being inexistent in source
 #define VOXELFLAG_INEXISTENT (1<<1)
 // Algorithm-dependent
+// flowWater: "visited"
 #define VOXELFLAG_CHECKED (1<<2)
+// Algorithm-dependent
+// getWaterPressure: "visited"
+#define VOXELFLAG_CHECKED2 (1<<3)
+// Algorithm-dependent
+// spreadWaterPressure: "visited"
+#define VOXELFLAG_CHECKED3 (1<<4)
+// Algorithm-dependent
+// water: "pressure check route node"
+#define VOXELFLAG_CHECKED4 (1<<5)
 
-class VoxelManipulator : public NodeContainer
+enum VoxelPrintMode
+{
+       VOXELPRINT_NOTHING,
+       VOXELPRINT_MATERIAL,
+       VOXELPRINT_WATERPRESSURE,
+};
+
+class VoxelManipulator /*: public NodeContainer*/
 {
 public:
        VoxelManipulator();
-       ~VoxelManipulator();
+       virtual ~VoxelManipulator();
        
        /*
                Virtuals from NodeContainer
        */
-       virtual u16 nodeContainerId() const
+       /*virtual u16 nodeContainerId() const
        {
                return NODECONTAINER_ID_VOXELMANIPULATOR;
        }
@@ -158,7 +318,7 @@ public:
        {
                emerge(p);
                return !(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT);
-       }
+       }*/
        // These are a bit slow and shouldn't be used internally
        MapNode getNode(v3s16 p)
        {
@@ -166,7 +326,7 @@ public:
 
                if(m_flags[m_area.index(p)] & VOXELFLAG_INEXISTENT)
                {
-                       dstream<<"ERROR: VoxelManipulator::getNode(): "
+                       dstream<<"EXCEPT: VoxelManipulator::getNode(): "
                                        <<"p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"
                                        <<", index="<<m_area.index(p)
                                        <<", flags="<<(int)m_flags[m_area.index(p)]
@@ -214,19 +374,80 @@ public:
                Control
        */
 
-       void clear();
-       
-       void print(std::ostream &o);
+       virtual void clear();
+
+       void print(std::ostream &o, VoxelPrintMode mode=VOXELPRINT_MATERIAL);
        
        void addArea(VoxelArea area);
 
+       /*
+               Copy data and set flags to 0
+               dst_area.getExtent() <= src_area.getExtent()
+       */
+       void copyFrom(MapNode *src, VoxelArea src_area,
+                       v3s16 from_pos, v3s16 to_pos, v3s16 size);
+
        /*
                Algorithms
        */
 
        void interpolate(VoxelArea area);
 
-       void flowWater(v3s16 removed_pos);
+       void clearFlag(u8 flag);
+       
+       // VOXELFLAG_CHECKED2s must usually be cleared before calling
+       // -1: dead end, 0-255: pressure
+       // highest_y: Highest found water y is stored here.
+       //            Must be initialized to -32768
+       int getWaterPressure(v3s16 p, s16 &highest_y, int recur_count);
+
+       /*
+               VOXELFLAG_CHECKED3s must usually be cleared before calling.
+
+               active_nodes: surface-touching air nodes with flow-causing
+               pressure. set-like dummy map container.
+
+               Spreads pressure pr at node p to request_area or as far as
+               there is invalid pressure.
+       */
+       void spreadWaterPressure(v3s16 p, int pr,
+                       VoxelArea request_area,
+                       core::map<v3s16, u8> &active_nodes,
+                       int recur_count);
+       
+       /*
+               VOXELFLAG_CHECKED3s must usually be cleared before calling.
+       */
+       void updateAreaWaterPressure(VoxelArea a,
+                       core::map<v3s16, u8> &active_nodes,
+                       bool checked3_is_clear=false);
+       
+       /*
+               Returns true if moved something
+       */
+       bool flowWater(v3s16 removed_pos,
+                       core::map<v3s16, u8> &active_nodes,
+                       int recursion_depth=0,
+                       bool debugprint=false, int *counter=NULL,
+                       int counterlimit=-1
+       );
+
+       /*
+               To flow some water, call this with the target node in
+               active_nodes
+               TODO: Make the active_nodes map to contain some vectors
+                     that are properly sorted according to water flow order.
+                         The current order makes water flow strangely if the
+                         first one is always taken.
+                         No, active_nodes should preserve the order stuff is
+                         added to it, in addition to adhering the water flow
+                         order.
+       */
+       void flowWater(core::map<v3s16, u8> &active_nodes,
+                       int recursion_depth=0,
+                       bool debugprint=false,
+                       int counterlimit=-1
+       );
 
        /*
                Virtual functions
@@ -265,32 +486,24 @@ public:
                MaxEdge is 1 higher than maximum allowed position
        */
        VoxelArea m_area;
+       
        /*
                NULL if data size is 0 (extent (0,0,0))
                Data is stored as [z*h*w + y*h + x]
        */
        MapNode *m_data;
+
        /*
                Flags of all nodes
        */
        u8 *m_flags;
+       
+       //TODO: Use these or remove them
+       //TODO: Would these make any speed improvement?
+       //bool m_pressure_route_valid;
+       //v3s16 m_pressure_route_surface;
 private:
 };
 
-class Map;
-
-class MapVoxelManipulator : public VoxelManipulator
-{
-public:
-       MapVoxelManipulator(Map *map);
-
-       virtual void emerge(VoxelArea a);
-
-       void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
-
-private:
-       Map *m_map;
-};
-
 #endif