1
0

Add WakeUp/AddBlock distinction

* WakeUp is for singular changes (block breaking for example). The simulator should check blocks around the position and discover other affected blocks as it sees fit
* AddBlock is for when you know a whole area is to be updated; chunk loading, or area wakeups for example
+ Prepares for correct handling of destroyed blocks after removal of SolidBlockHandler in the redstone simulator
This commit is contained in:
Tiger Wang 2020-07-29 00:12:45 +01:00
parent 6d7b83a69d
commit 99856df686
20 changed files with 144 additions and 124 deletions

View File

@ -1330,7 +1330,7 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
);
// Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391):
m_World->GetSimulatorManager()->WakeUpArea(cCuboid(
m_World->GetSimulatorManager()->WakeUp(cCuboid(
{bx - ExplosionSizeInt - 1, MinY, bz - ExplosionSizeInt - 1},
{bx + ExplosionSizeInt + 1, MaxY, bz + ExplosionSizeInt + 1}
));

View File

@ -111,7 +111,6 @@ void cMultiVersionProtocol::HandleIncomingDataInRecognitionStage(cClientHandle &
// The protocol recogniser succesfully identified, switch mode:
HandleIncomingData = [this](cClientHandle &, const std::string_view a_In)
{
// TODO: make it take our a_ReceivedData
m_Protocol->DataReceived(m_Buffer, a_In.data(), a_In.size());
};
}

View File

@ -78,19 +78,8 @@ cDelayedFluidSimulator::cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Flu
void cDelayedFluidSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
void cDelayedFluidSimulator::AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
if ((a_Block.y < 0) || (a_Block.y >= cChunkDef::Height))
{
// Not inside the world (may happen when rclk with a full bucket - the client sends Y = -1)
return;
}
if ((a_Chunk == nullptr) || !a_Chunk->IsValid())
{
return;
}
int RelX = a_Block.x - a_Chunk->GetPosX() * cChunkDef::Width;
int RelZ = a_Block.z - a_Chunk->GetPosZ() * cChunkDef::Width;
BLOCKTYPE BlockType = a_Chunk->GetBlock(RelX, a_Block.y, RelZ);
@ -156,3 +145,14 @@ void cDelayedFluidSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a
void cDelayedFluidSimulator::WakeUp(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
if (!cChunkDef::IsValidHeight(a_Position.y))
{
// Not inside the world (may happen when rclk with a full bucket - the client sends Y = -1)
return;
}
Super::WakeUp(a_Chunk, a_Position, a_Block);
}

View File

@ -55,7 +55,8 @@ public:
cDelayedFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, int a_TickDelay);
// cSimulator overrides:
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override;
virtual void WakeUp(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override;
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override;
virtual void Simulate(float a_Dt) override;
virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); }

View File

@ -239,7 +239,7 @@ bool cFireSimulator::DoesBurnForever(BLOCKTYPE a_BlockType)
void cFireSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
void cFireSimulator::AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
if ((a_Chunk == nullptr) || !a_Chunk->IsValid())
{

View File

@ -43,8 +43,7 @@ protected:
/** Chance [0..100000] of a fuel burning out being replaced by a new fire block instead of an air block */
int m_ReplaceFuelChance;
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override;
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override;
/** Returns the time [msec] after which the specified fire block is stepped again; based on surrounding fuels */
int GetBurnStepTime(cChunk * a_Chunk, Vector3i a_RelPos);

View File

@ -128,6 +128,53 @@ std::unique_ptr<cRedstoneHandler> cIncrementalRedstoneSimulator::CreateComponent
void cIncrementalRedstoneSimulator::WakeUp(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
Super::WakeUp(a_Chunk, a_Position, a_Block);
auto & ChunkData = *static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_Chunk.GetRedstoneSimulatorData());
// Never update blocks without a handler:
if (GetComponentHandler(a_Block) == nullptr)
{
ChunkData.ErasePowerData(a_Position);
return;
}
// Only update others if there is a redstone device nearby
for (int x = -1; x < 2; ++x)
{
for (int y = -1; y < 2; ++y)
{
if (!cChunkDef::IsValidHeight(a_Position.y + y))
{
continue;
}
for (int z = -1; z < 2; ++z)
{
auto CheckPos = a_Position + Vector3i{ x, y, z };
BLOCKTYPE Block;
NIBBLETYPE Meta;
// If we can't read the block, assume it is a mechanism
if (
!a_Chunk.UnboundedRelGetBlock(CheckPos, Block, Meta) ||
IsRedstone(Block)
)
{
ChunkData.WakeUp(a_Position);
return;
}
}
}
}
}
void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
auto & ChunkData = *static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_Chunk->GetRedstoneSimulatorData());
@ -224,7 +271,7 @@ void cIncrementalRedstoneSimulator::ProcessWorkItem(cChunk & Chunk, cChunk & Tic
void cIncrementalRedstoneSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
void cIncrementalRedstoneSimulator::AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
// Can't inspect block, ignore:
if ((a_Chunk == nullptr) || !a_Chunk->IsValid())
@ -236,50 +283,16 @@ void cIncrementalRedstoneSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
const auto Relative = cChunkDef::AbsoluteToRelative(a_Block, a_Chunk->GetPos());
const auto CurrentBlock = a_Chunk->GetBlock(Relative);
// Always update redstone devices
if (IsRedstone(CurrentBlock))
if (!IsRedstone(CurrentBlock))
{
if (IsAlwaysTicked(CurrentBlock))
{
ChunkData.AlwaysTickedPositions.emplace(Relative);
}
ChunkData.WakeUp(Relative);
return;
}
// Never update blocks without a handler
if (GetComponentHandler(CurrentBlock) == nullptr)
if (IsAlwaysTicked(CurrentBlock))
{
ChunkData.ErasePowerData(Relative);
return;
ChunkData.AlwaysTickedPositions.emplace(Relative);
}
// Only update others if there is a redstone device nearby
for (int x = -1; x < 2; ++x)
{
for (int y = -1; y < 2; ++y)
{
if (!cChunkDef::IsValidHeight(Relative.y + y))
{
continue;
}
for (int z = -1; z < 2; ++z)
{
auto CheckPos = Relative + Vector3i{x, y, z};
BLOCKTYPE Block;
NIBBLETYPE Meta;
// If we can't read the block, assume it is a mechanism
if (
!a_Chunk->UnboundedRelGetBlock(CheckPos, Block, Meta) ||
IsRedstone(Block)
)
{
ChunkData.WakeUp(Relative);
return;
}
}
}
}
// Always update redstone devices:
ChunkData.WakeUp(Relative);
}

View File

@ -21,6 +21,10 @@ public:
{
}
private:
virtual void WakeUp(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override;
virtual void Simulate(float Dt) override {};
virtual void SimulateChunk(std::chrono::milliseconds Dt, int ChunkX, int ChunkZ, cChunk * Chunk) override;
@ -36,7 +40,7 @@ public:
return IsRedstone(a_BlockType);
}
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override;
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override;
/** Returns if a block is a mechanism (something that accepts power and does something)
Used by torches to determine if they will power a block */

View File

@ -28,7 +28,7 @@ public:
}
// cSimulator overrides:
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override
{
UNUSED(a_Block);
UNUSED(a_Chunk);

View File

@ -28,7 +28,7 @@ public:
UNUSED(a_Chunk);
}
virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType) override { return false; }
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override
{
UNUSED(a_Block);
UNUSED(a_Chunk);

View File

@ -31,11 +31,6 @@ public:
{
}
virtual void Simulate(float a_Dt) = 0;
virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) = 0;
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) = 0;
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) = 0;
virtual cRedstoneSimulatorChunkData * CreateChunkData() = 0;
};

View File

@ -97,7 +97,7 @@ bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
void cSandSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
void cSandSimulator::AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
if ((a_Chunk == nullptr) || !a_Chunk->IsValid())
{

View File

@ -27,6 +27,7 @@ class cSandSimulator :
public cSimulator
{
public:
cSandSimulator(cWorld & a_World, cIniFile & a_IniFile);
// cSimulator overrides:
@ -56,11 +57,12 @@ public:
);
protected:
bool m_IsInstantFall; // If set to true, blocks don't fall using cFallingBlock entity, but instantly instead
int m_TotalBlocks; // Total number of blocks currently in the queue for simulating
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override;
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override;
/** Performs the instant fall of the block - removes it from top, Finishes it at the bottom */
void DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ);

View File

@ -1,33 +1,38 @@
#include "Globals.h"
#include "../World.h"
#include "../Defines.h"
#include "Simulator.h"
#include "../Chunk.h"
#include "../Cuboid.h"
#ifdef __clang__
#pragma clang diagnostic ignored "-Wweak-template-vtables"
#endif // __clang__
#include "Simulator.h"
#include "../World.h"
void cSimulator::WakeUp(Vector3i a_Block, cChunk * a_Chunk)
void cSimulator::WakeUp(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
AddBlock(a_Block, a_Chunk);
ASSERT(a_Chunk.IsValid());
AddBlock(a_Chunk, a_Position, a_Block);
}
void cSimulator::WakeUpArea(const cCuboid & a_Area)
void cSimulator::WakeUp(cChunk & a_Chunk, Vector3i a_Position, Vector3i a_Offset, BLOCKTYPE a_Block)
{
ASSERT(a_Chunk.IsValid());
WakeUp(a_Chunk, a_Position, a_Block);
}
void cSimulator::WakeUp(const cCuboid & a_Area)
{
cCuboid area(a_Area);
area.Sort();
@ -60,7 +65,8 @@ void cSimulator::WakeUpArea(const cCuboid & a_Area)
{
for (int x = startX; x <= endX; ++x)
{
AddBlock({x, y, z}, &a_CBChunk);
const auto Position = cChunkDef::AbsoluteToRelative({ x, y, z });
AddBlock(a_CBChunk, Position, a_CBChunk.GetBlock(Position));
} // for x
} // for z
} // for y
@ -70,7 +76,3 @@ void cSimulator::WakeUpArea(const cCuboid & a_Area)
} // for cx
} // for cz
}

View File

@ -19,6 +19,7 @@ may update its internal state based on this call. */
class cSimulator
{
public:
cSimulator(cWorld & a_World)
: m_World(a_World)
{
@ -26,10 +27,13 @@ public:
virtual ~cSimulator() {}
/** Called in each tick, a_Dt is the time passed since the last tick, in msec */
virtual void Simulate(float a_Dt) = 0;
virtual void WakeUp(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block);
/** Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available */
protected:
friend class cChunk; // Calls AddBlock() in its WakeUpSimulators() function, to speed things up
virtual void Simulate(float a_Dt) = 0;
virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
UNUSED(a_Dt);
@ -38,28 +42,26 @@ public:
UNUSED(a_Chunk);
}
/** Called when a block changes */
void WakeUp(Vector3i a_Block, 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:
friend class cChunk; // Calls AddBlock() in its WakeUpSimulators() function, to speed things up
/** Called to simulate a new block. Unlike WakeUp this function will perform minimal checking.
It queues the block to be simulated as fast as possible, only making sure that the block type IsAllowedBlock. */
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) = 0;
/** Called to simulate a new block */
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) = 0;
/** Called to simulate a single new block, typically as a result of a single block break or change.
The simulator implementation may decide to perform additional checks or maintain consistency of internal state
before the block is added to the simulate queue. */
virtual void WakeUp(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block);
/** Called to simulate a single block, synthesised by the simulator manager.
The position represents the adjacents of the block that was actually changed, with the offset used given.
Simulators may use this information to update additional blocks that were affected by the change, or queue
farther, extra-adjacents blocks to be updated. The simulator manager calls this overload after the 3-argument WakeUp. */
virtual void WakeUp(cChunk & a_Chunk, Vector3i a_Position, Vector3i a_Offset, BLOCKTYPE a_Block);
/** Called to simulate an area by the manager, delegated to cSimulator to avoid virtual calls in tight loops. */
void WakeUp(const cCuboid & a_Area);
cWorld & m_World;
} ;

View File

@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "SimulatorManager.h"
#include "../Chunk.h"
#include "../World.h"
@ -58,11 +59,13 @@ void cSimulatorManager::SimulateChunk(std::chrono::milliseconds a_Dt, int a_Chun
void cSimulatorManager::WakeUp(Vector3i a_Block, cChunk * a_Chunk)
void cSimulatorManager::WakeUp(cChunk & a_Chunk, Vector3i a_Position)
{
ASSERT(a_Chunk.IsValid());
for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr)
{
itr->first->WakeUp(a_Block, a_Chunk);
itr->first->WakeUp(a_Chunk, a_Position, a_Chunk.GetBlock(a_Position));
}
}
@ -70,11 +73,11 @@ void cSimulatorManager::WakeUp(Vector3i a_Block, cChunk * a_Chunk)
void cSimulatorManager::WakeUpArea(const cCuboid & a_Area)
void cSimulatorManager::WakeUp(const cCuboid & a_Area)
{
for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr)
for (const auto Item : m_Simulators)
{
itr->first->WakeUpArea(a_Area);
Item.first->WakeUp(a_Area);
}
}
@ -86,7 +89,3 @@ void cSimulatorManager::RegisterSimulator(cSimulator * a_Simulator, int a_Rate)
{
m_Simulators.push_back(std::make_pair(a_Simulator, a_Rate));
}

View File

@ -28,26 +28,30 @@ class cWorld;
class cSimulatorManager
{
public:
cSimulatorManager(cWorld & a_World);
~cSimulatorManager();
/** Called in each tick, a_Dt is the time passed since the last tick, in msec. */
void Simulate(float a_Dt);
/** Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available. */
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(Vector3i a_Block, cChunk * a_Chunk);
/* Called when a single block changes, wakes all simulators up for the block.
The simulator implementation may also decide to wake the block's face-neighbors or blocks farther away. */
void WakeUp(cChunk & a_Chunk, Vector3i a_Position);
/** 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);
Note that, unlike WakeUp(), this call adds blocks not only face-neighboring, but also edge-neighboring and corner-neighboring the specified area. */
void WakeUp(const cCuboid & a_Area);
void RegisterSimulator(cSimulator * a_Simulator, int a_Rate); // Takes ownership of the simulator object!
protected:
typedef std::vector <std::pair<cSimulator *, int> > cSimulators;
cWorld & m_World;

View File

@ -22,7 +22,7 @@ cVaporizeFluidSimulator::cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_F
void cVaporizeFluidSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
void cVaporizeFluidSimulator::AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
if (a_Chunk == nullptr)
{

View File

@ -26,7 +26,7 @@ public:
cVaporizeFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid);
// cSimulator overrides:
virtual void AddBlock(Vector3i a_Block, cChunk * a_Chunk) override;
virtual void AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block) override;
virtual void Simulate(float a_Dt) override;
} ;

View File

@ -1327,7 +1327,7 @@ void cWorld::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinB
void cWorld::WakeUpSimulatorsInArea(const cCuboid & a_Area)
{
m_SimulatorManager->WakeUpArea(a_Area);
m_SimulatorManager->WakeUp(a_Area);
}