From 2a0d1a059e556afaeb7f5b72205b26447e23286f Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 11 Dec 2010 18:11:03 +0200 Subject: commit before some radicallish changes to water behavior --- src/exceptions.h | 8 + src/main.cpp | 5 +- src/map.cpp | 149 +++++++++- src/map.h | 34 ++- src/mapblock.cpp | 35 +++ src/mapblock.h | 68 +++-- src/mapnode.h | 24 +- src/serialization.h | 3 +- src/server.cpp | 96 +++++++ src/server.h | 3 + src/test.cpp | 77 ++++- src/utility.h | 29 +- src/voxel.cpp | 788 ++++++++++++++++++++++++++++++++++++++++++++++------ src/voxel.h | 267 ++++++++++++++++-- 14 files changed, 1420 insertions(+), 166 deletions(-) (limited to 'src') diff --git a/src/exceptions.h b/src/exceptions.h index 80bdbeb36..0f95bd07a 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -116,6 +116,14 @@ public: {} }; +class ProcessingLimitException : public BaseException +{ +public: + ProcessingLimitException(const char *s): + BaseException(s) + {} +}; + /* Some "old-style" interrupts: */ diff --git a/src/main.cpp b/src/main.cpp index 938eb14ef..73ef37951 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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) - Does their position get saved to a border value or something? -TODO: MovingObject::move and Player::move are basically the same. +SUGG: MovingObject::move and Player::move are basically the same. combine them. +TODO: Transfer sign texts as metadata of block and not as data of + object + Doing now: ====================================================================== diff --git a/src/map.cpp b/src/map.cpp index 050299bc9..88cb0f3f7 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -47,10 +47,10 @@ MapBlockPointerCache::~MapBlockPointerCache() { m_map->m_blockcachelock.cacheRemoved(); - dstream<<"MapBlockPointerCache:" + /*dstream<<"MapBlockPointerCache:" <<" from_cache_count="<::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 ("<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"< & 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 diff --git a/src/map.h b/src/map.h index 7f791ffad..62d1f8aee 100644 --- 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"< 0); @@ -147,7 +149,7 @@ public: if(m_count == 0) m_cache_mutex.Unlock(); - dstream<<"cacheRemoved() end"< & 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 m_loaded_blocks; +}; + #endif diff --git a/src/mapblock.cpp b/src/mapblock.cpp index a4da657d1..d2c323291 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -696,6 +696,15 @@ bool MapBlock::propagateSunlight(core::map & 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 pressuredata(nodecount); + for(u32 i=0; i= 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 & 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) diff --git a/src/mapnode.h b/src/mapnode.h index 02abe4e52..7502c42d7 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -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]; } } }; diff --git a/src/serialization.h b/src/serialization.h index 6e18497cf..8d3c5fee2 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -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 diff --git a/src/server.cpp b/src/server.cpp index f8248acb4..8bcfe5216 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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 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"< lighting_modified_blocks; + map.updateLighting(modified_blocks, lighting_modified_blocks); + + // Add blocks modified by lighting to modified_blocks + for(core::map::Iterator + i = lighting_modified_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + modified_blocks.insert(block->getPos(), block); + } + } + + /* + Set the modified blocks unsent for all the clients + */ + + JMutexAutoLock lock2(m_con_mutex); + + for(core::map::Iterator + i = m_clients.getIterator(); + i.atEnd() == false; i++) + { + RemoteClient *client = i.getNode()->getValue(); + + if(modified_blocks.size() > 0) + { + // Remove block from sent history + client->SetBlocksNotSent(modified_blocks); + } + } + + } + } // 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"< m_flow_active_nodes; + friend class EmergeThread; friend class RemoteClient; }; diff --git a/src/test.cpp b/src/test.cpp index 6b285e3a4..ebefb8e32 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -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 aa; + d.diff(c, aa); + + // Correct results + core::array 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:"<::Iterator + i = aa.begin(); i != aa.end(); i++) + { + i->print(dstream); + dstream< 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:"<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 "<getTimer()->getRealTime(); u32 dtime = time2 - m_time1; - if(quiet == false) - std::cout< 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 &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 &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"< 255) + pr = 255; + + /*dstream<<"WARNING: Pressure at (" + < &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="<=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" + < counterlimit) + { + dstream<<"Counter limit reached; returning"< &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="<= 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 & 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:"<=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" + <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"< &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() = "<::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::Iterator + i = active_nodes.getIterator().getNode(); + for(s32 j=0; j::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"< +#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 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 &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<<"("<