From 167c4bf2e691e22240a3c41ebc7181a03933fdb4 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Fri, 14 Jul 2017 16:18:33 +0200 Subject: [PATCH] Simulators: Added area-based wakeup. --- src/Chunk.cpp | 1 - src/ChunkMap.cpp | 51 +++--------------------------- src/ChunkMap.h | 3 -- src/Simulator/Simulator.cpp | 50 +++++++++++++++++++++++++++++ src/Simulator/Simulator.h | 20 ++++++++++-- src/Simulator/SimulatorManager.cpp | 12 +++++++ src/Simulator/SimulatorManager.h | 8 +++++ src/World.cpp | 2 +- src/World.h | 2 +- 9 files changed, 93 insertions(+), 56 deletions(-) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 9ddee2343..282293c0f 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -430,7 +430,6 @@ void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock int BaseZ = BlockStartZ - a_MinBlockZ; // Copy blocktype and blockmeta: - // TODO: Right now each changed block is transmitted to all clients as a separate packet. Optimize this for larger areas. BLOCKTYPE * AreaBlockTypes = a_Area.GetBlockTypes(); NIBBLETYPE * AreaBlockMetas = a_Area.GetBlockMetas(); for (int y = 0; y < SizeY; y++) diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index f04af4340..491bc4752 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -757,48 +757,6 @@ void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) -void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) -{ - // Limit the Y coords: - a_MinBlockY = std::max(a_MinBlockY, 0); - a_MaxBlockY = std::min(a_MaxBlockY, cChunkDef::Height - 1); - - cSimulatorManager * SimMgr = m_World->GetSimulatorManager(); - int MinChunkX, MinChunkZ, MaxChunkX, MaxChunkZ; - cChunkDef::BlockToChunk(a_MinBlockX, a_MinBlockZ, MinChunkX, MinChunkZ); - cChunkDef::BlockToChunk(a_MaxBlockX, a_MaxBlockZ, MaxChunkX, MaxChunkZ); - cCSLock Lock(m_CSChunks); - for (int z = MinChunkZ; z <= MaxChunkZ; z++) - { - int MinZ = std::max(a_MinBlockZ, z * cChunkDef::Width); - int MaxZ = std::min(a_MaxBlockZ, z * cChunkDef::Width + cChunkDef::Width - 1); - for (int x = MinChunkX; x <= MaxChunkX; x++) - { - cChunkPtr Chunk = GetChunkNoGen(x, z); - if ((Chunk == nullptr) || !Chunk->IsValid()) - { - continue; - } - int MinX = std::max(a_MinBlockX, x * cChunkDef::Width); - int MaxX = std::min(a_MaxBlockX, x * cChunkDef::Width + cChunkDef::Width - 1); - for (int BlockY = a_MinBlockY; BlockY <= a_MaxBlockY; BlockY++) - { - for (int BlockZ = MinZ; BlockZ <= MaxZ; BlockZ++) - { - for (int BlockX = MinX; BlockX <= MaxX; BlockX++) - { - SimMgr->WakeUp(BlockX, BlockY, BlockZ, Chunk); - } // for BlockX - } // for BlockZ - } // for BlockY - } // for x - chunks - } // for z = chunks -} - - - - - void cChunkMap::MarkChunkDirty(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSChunks); @@ -1863,11 +1821,10 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_ ForEachEntity(TNTDamageCallback); // Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391): - WakeUpSimulatorsInArea( - bx - ExplosionSizeInt - 1, bx + ExplosionSizeInt + 1, - MinY, MaxY, - bz - ExplosionSizeInt - 1, bz + ExplosionSizeInt + 1 - ); + m_World->GetSimulatorManager()->WakeUpArea(cCuboid( + bx - ExplosionSizeInt - 1, MinY, bz - ExplosionSizeInt - 1, + bx + ExplosionSizeInt + 1, MaxY, bz + ExplosionSizeInt + 1 + )); } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 7c57f9669..130c4b062 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -114,9 +114,6 @@ public: /** Wakes up simulators for the specified block */ void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Wakes up the simulators for the specified area of blocks */ - void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); - void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); void MarkChunkSaving (int a_ChunkX, int a_ChunkZ); void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); diff --git a/src/Simulator/Simulator.cpp b/src/Simulator/Simulator.cpp index 0175fd67d..275d60161 100644 --- a/src/Simulator/Simulator.cpp +++ b/src/Simulator/Simulator.cpp @@ -5,6 +5,7 @@ #include "../BlockID.h" #include "../Defines.h" #include "../Chunk.h" +#include "../Cuboid.h" #ifdef __clang__ #pragma clang diagnostic ignored "-Wweak-template-vtables" @@ -38,3 +39,52 @@ void cSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chu + +void cSimulator::WakeUpArea(const cCuboid & a_Area) +{ + cCuboid area(a_Area); + area.Sort(); + area.Expand(1, 1, 1, 1, 1, 1); // Expand the area to contain the neighbors, too. + area.ClampY(0, cChunkDef::Height - 1); + + // Add all blocks, in a per-chunk manner: + int chunkStartX, chunkStartZ, chunkEndX, chunkEndZ; + cChunkDef::BlockToChunk(area.p1.x, area.p1.z, chunkStartX, chunkStartZ); + cChunkDef::BlockToChunk(area.p2.x, area.p2.z, chunkEndX, chunkEndZ); + for (int cz = chunkStartZ; cz <= chunkEndZ; ++cz) + { + for (int cx = chunkStartX; cx <= chunkEndX; ++cx) + { + m_World.DoWithChunk(cx, cz, [this, &area](cChunk & a_CBChunk) -> bool + { + if (!a_CBChunk.IsValid()) + { + LOGWARNING("%s: Trying to wake up inside a non-valid chunk [%d, %d]. Ignoring.", + __FUNCTION__, a_CBChunk.GetPosX(), a_CBChunk.GetPosZ() + ); + return true; + } + int startX = std::max(area.p1.x, a_CBChunk.GetPosX() * cChunkDef::Width); + int startZ = std::max(area.p1.z, a_CBChunk.GetPosZ() * cChunkDef::Width); + int endX = std::min(area.p2.x, a_CBChunk.GetPosX() * cChunkDef::Width + cChunkDef::Width - 1); + int endZ = std::min(area.p2.z, a_CBChunk.GetPosZ() * cChunkDef::Width + cChunkDef::Width - 1); + for (int y = area.p1.y; y <= area.p2.y; ++y) + { + for (int z = startZ; z <= endZ; ++z) + { + for (int x = startX; x <= endX; ++x) + { + AddBlock(x, y, z, &a_CBChunk); + } // for x + } // for z + } // for y + return true; + } // lambda + ); // DoWithChunk + } // for cx + } // for cz +} + + + + diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h index b0e3b16f4..ef0a3bf68 100644 --- a/src/Simulator/Simulator.h +++ b/src/Simulator/Simulator.h @@ -1,15 +1,21 @@ #pragma once -#include "../Vector3.h" - class cWorld; class cChunk; +class cCuboid; +/** Base class for all block-based physics simulators (such as fluid, fire, falling blocks etc.). +Each descendant provides an implementation of what needs to be done on each world tick. +The descendant may choose to do all processing in a single call for the entire world (Simulate()) +or do per-chunk calculations (SimulateChunk()). +Whenever a block is changed, the WakeUp() or WakeUpArea() functions are called to notify all simulators. +The functions add all affected blocks and all their direct neighbors using the AddBlock() function. The simulator +may update its internal state based on this call. */ class cSimulator { public: @@ -33,8 +39,16 @@ public: } /** Called when a block changes */ - virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); + void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); + /** Does the same processing as WakeUp, but for all blocks within the specified area. + Has better performance than calling WakeUp for each block individually, due to neighbor-checking. + All chunks intersected by the area should be valid (outputs a warning if not). + Note that, unlike WakeUp(), this call adds blocks not only face-neighboring, but also edge-neighboring and + corner-neighboring the specified area. So far none of the simulators care about that. */ + void WakeUpArea(const cCuboid & a_Area); + + /** Returns true if the specified block type is "interesting" for this simulator. */ virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) = 0; protected: diff --git a/src/Simulator/SimulatorManager.cpp b/src/Simulator/SimulatorManager.cpp index e74642fc0..78c02fc07 100644 --- a/src/Simulator/SimulatorManager.cpp +++ b/src/Simulator/SimulatorManager.cpp @@ -70,6 +70,18 @@ void cSimulatorManager::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk +void cSimulatorManager::WakeUpArea(const cCuboid & a_Area) +{ + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr) + { + itr->first->WakeUpArea(a_Area); + } +} + + + + + void cSimulatorManager::RegisterSimulator(cSimulator * a_Simulator, int a_Rate) { m_Simulators.push_back(std::make_pair(a_Simulator, a_Rate)); diff --git a/src/Simulator/SimulatorManager.h b/src/Simulator/SimulatorManager.h index e6ad68bf3..daa949157 100644 --- a/src/Simulator/SimulatorManager.h +++ b/src/Simulator/SimulatorManager.h @@ -35,8 +35,16 @@ public: void SimulateChunk(std::chrono::milliseconds a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk); + /* Called when a single block changes, wakes all simulators up for the block and its face-neighbors. */ void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); + /** Does the same processing as WakeUp, but for all blocks within the specified area. + Has better performance than calling WakeUp for each block individually, due to neighbor-checking. + All chunks intersected by the area should be valid (outputs a warning if not). + Note that, unlike WakeUp(), this call adds blocks not only face-neighboring, but also edge-neighboring and + corner-neighboring the specified area. So far none of the simulators care about that. */ + void WakeUpArea(const cCuboid & a_Area); + void RegisterSimulator(cSimulator * a_Simulator, int a_Rate); // Takes ownership of the simulator object! protected: diff --git a/src/World.cpp b/src/World.cpp index da7d8fb2d..3f26fe809 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1305,7 +1305,7 @@ void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) void cWorld::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ) { - return m_ChunkMap->WakeUpSimulatorsInArea(a_MinBlockX, a_MaxBlockX, a_MinBlockY, a_MaxBlockY, a_MinBlockZ, a_MaxBlockZ); + m_SimulatorManager->WakeUpArea(cCuboid(a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ)); } diff --git a/src/World.h b/src/World.h index a3180e008..7d8d8bf7f 100644 --- a/src/World.h +++ b/src/World.h @@ -432,7 +432,7 @@ public: Returns true if all chunks have been processed. Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too. a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together. - */ + Doesn't wake up simulators, use WakeUpSimulatorsInArea() for that. */ virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) override; // tolua_begin