1
0

Major refactoring of redstone

This commit is a refactoring of the redstone code, mainly the functions
handling the removal of invalid blocks from power supplier data
structures. Its aim is to improve performance and potentially reduce the
memory footprint of the data structures.

It works to reduce the amount of GetBlock()s triggered every tick.
Before, a GetBlock() was requested for every single item in the data
lists, as well as for every single redstone block in a chunk. Following
these changes, the AddBlock() event is utilised more effectively to only
update the lists when needed (a block is changed), as well as to insert
the block type (and update it when needed) alongside the coordinates
into the main redstone simulator chunkdata list.

In short, a single GetBlock() is now cached, with this cache being
updated when the simulator is awoken due to a block change.

At least, I *hope* that this is what it does :P
This commit is contained in:
Tiger Wang
2014-01-10 20:31:05 +00:00
parent 71946f5671
commit e7fba08e6c
4 changed files with 183 additions and 188 deletions

View File

@@ -44,83 +44,45 @@ void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChu
int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
BLOCKTYPE Block;
NIBBLETYPE Meta;
a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta);
if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ)))
{
return;
}
// Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid
// Checking only when a block is changed, as opposed to every tick, also improves performance
// Check for duplicates:
cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
for (cRedstoneSimulatorChunkData::const_iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
{
if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
{
return;
}
}
ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ));
}
void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
if (ChunkData.empty())
{
return;
}
int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
// Check to see if PoweredBlocks have invalid items (source is air or unpowered)
for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end();)
{
int RelX = itr->a_SourcePos.x - a_ChunkX * cChunkDef::Width;
int RelZ = itr->a_SourcePos.z - a_ChunkZ * cChunkDef::Width;
int DestRelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width;
int DestRelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width;
BLOCKTYPE SourceBlockType;
NIBBLETYPE SourceBlockMeta;
BLOCKTYPE DestBlockType;
if (
!a_Chunk->UnboundedRelGetBlock(RelX, itr->a_SourcePos.y, RelZ, SourceBlockType, SourceBlockMeta) ||
!a_Chunk->UnboundedRelGetBlockType(DestRelX, itr->a_BlockPos.y, DestRelZ, DestBlockType)
)
if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
++itr;
continue;
}
if (SourceBlockType != itr->a_SourceBlock)
if (!IsPotentialSource(Block))
{
LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past block type mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = m_PoweredBlocks.erase(itr);
}
else if (
// Changeable sources
((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (SourceBlockMeta == 0)) ||
((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(SourceBlockMeta)) ||
((SourceBlockType == E_BLOCK_DETECTOR_RAIL) && (SourceBlockMeta & 0x08) == 0x08) ||
(((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta))) ||
(((SourceBlockType == E_BLOCK_STONE_PRESSURE_PLATE) || (SourceBlockType == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (SourceBlockMeta == 0))
((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) ||
((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) ||
((Block == E_BLOCK_DETECTOR_RAIL) && (Meta & 0x08) == 0x08) ||
(((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) ||
(((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0))
)
{
LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = m_PoweredBlocks.erase(itr);
}
else if (SourceBlockType == E_BLOCK_DAYLIGHT_SENSOR)
else if (Block == E_BLOCK_DAYLIGHT_SENSOR)
{
if (!a_Chunk->IsLightValid())
{
m_World.QueueLightChunk(a_ChunkX, a_ChunkZ);
m_World.QueueLightChunk(a_Chunk->GetPosX(), a_Chunk->GetPosZ());
++itr;
continue;
}
else
{
@@ -135,60 +97,50 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
else
{
++itr;
continue;
}
}
}
else if ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (DestBlockType == E_BLOCK_REDSTONE_WIRE))
{
// It is simply not allowed that a wire powers another wire, presuming that data here is sane and a dest and source are beside each other
LOGD("cRedstoneSimulator: Erased redstone wire from powered blocks list because its source was also wire");
itr = m_PoweredBlocks.erase(itr);
}
else
{
++itr;
}
}
// Check to see if LinkedPoweredBlocks have invalid items: source, block powered through, or power destination block has changed
for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end();)
{
int RelX = itr->a_SourcePos.x - a_ChunkX * cChunkDef::Width;
int RelZ = itr->a_SourcePos.z - a_ChunkZ * cChunkDef::Width;
int MidRelX = itr->a_MiddlePos.x - a_ChunkX * cChunkDef::Width;
int MidRelZ = itr->a_MiddlePos.z - a_ChunkZ * cChunkDef::Width;
BLOCKTYPE SourceBlockType;
NIBBLETYPE SourceBlockMeta;
BLOCKTYPE MiddleBlockType;
if (
!a_Chunk->UnboundedRelGetBlock(RelX, itr->a_SourcePos.y, RelZ, SourceBlockType, SourceBlockMeta) ||
!a_Chunk->UnboundedRelGetBlockType(MidRelX, itr->a_MiddlePos.y, MidRelZ, MiddleBlockType)
)
if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
continue;
if (!IsPotentialSource(Block))
{
LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = m_LinkedPoweredBlocks.erase(itr);
}
else if (
// Things that can send power through a block but which depends on meta
((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) ||
((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) ||
(((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta)))
)
{
LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = m_LinkedPoweredBlocks.erase(itr);
}
else
{
++itr;
}
}
if (SourceBlockType != itr->a_SourceBlock)
else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past block type mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
itr = m_LinkedPoweredBlocks.erase(itr);
}
else if (MiddleBlockType != itr->a_MiddleBlock)
{
LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past middle block mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
itr = m_LinkedPoweredBlocks.erase(itr);
}
else if (
// Things that can send power through a block but which depends on meta
((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (SourceBlockMeta == 0)) ||
((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(SourceBlockMeta)) ||
(((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta)))
)
{
LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
itr = m_LinkedPoweredBlocks.erase(itr);
if (!IsViableMiddleBlock(Block))
{
LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = m_LinkedPoweredBlocks.erase(itr);
}
else
{
++itr;
}
}
else
{
@@ -196,60 +148,85 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
}
}
for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end();)
for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr)
{
int RelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width;
int RelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width;
BLOCKTYPE SourceBlockType;
if (!a_Chunk->UnboundedRelGetBlockType(RelX, itr->a_BlockPos.y, RelZ, SourceBlockType))
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
continue;
}
else if (!IsAllowedBlock(SourceBlockType))
if (!IsAllowedBlock(Block))
{
LOGD("cRedstoneSimulator: Erased block %s from toggleable simulated list as block is no longer redstone", ItemToFullString(SourceBlockType).c_str());
LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from toggleable simulated list as it is no longer redstone", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
itr = m_SimulatedPlayerToggleableBlocks.erase(itr);
}
else
{
++itr;
break;
}
}
for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end();)
for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr)
{
int RelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width;
int RelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width;
BLOCKTYPE SourceBlockType;
if (!a_Chunk->UnboundedRelGetBlockType(RelX, itr->a_BlockPos.y, RelZ, SourceBlockType))
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
continue;
}
if ((SourceBlockType != E_BLOCK_REDSTONE_REPEATER_ON) && (SourceBlockType != E_BLOCK_REDSTONE_REPEATER_OFF))
if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF))
{
itr = m_RepeatersDelayList.erase(itr);
continue;
break;
}
++itr;
}
cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
{
if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block
{
if (!IsAllowedBlock(Block))
{
ChunkData.erase(itr); // The new blocktype is not redstone; it must be removed from this list
}
else
{
itr->Data = Block; // Update block information
}
return;
}
}
if (!IsAllowedBlock(Block))
{
return;
}
ChunkData.push_back(cCoordWithBlock(RelX, a_BlockY, RelZ, Block));
}
void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
{
// We still attempt to simulate all blocks in the chunk every tick, because of outside influence that needs to be taken into account
// For example, repeaters need to be ticked, pressure plates checked for entities, daylight sensor checked for light changes, etc.
// The easiest way to make this more efficient is probably just to reduce code within the handlers that put too much strain on server, like getting or setting blocks
// A marking dirty system might be a TODO for later on, perhaps
cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
if (ChunkData.empty())
{
return;
}
int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(), end = ChunkData.end(); dataitr != end;)
{
BLOCKTYPE BlockType = a_Chunk->GetBlock(dataitr->x, dataitr->y, dataitr->z);
if (!IsAllowedBlock(BlockType))
{
dataitr = ChunkData.erase(dataitr);
continue;
}
// PoweredBlock and LinkedPoweredBlock list was fine, now to the actual handling
int a_X = BaseX + dataitr->x;
int a_Z = BaseZ + dataitr->z;
switch (BlockType)
switch (dataitr->Data)
{
case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break;
case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break;
@@ -262,19 +239,19 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
case E_BLOCK_REDSTONE_TORCH_OFF:
case E_BLOCK_REDSTONE_TORCH_ON:
{
HandleRedstoneTorch(a_X, dataitr->y, a_Z, BlockType);
HandleRedstoneTorch(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_STONE_BUTTON:
case E_BLOCK_WOODEN_BUTTON:
{
HandleRedstoneButton(a_X, dataitr->y, a_Z, BlockType);
HandleRedstoneButton(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_REDSTONE_REPEATER_OFF:
case E_BLOCK_REDSTONE_REPEATER_ON:
{
HandleRedstoneRepeater(a_X, dataitr->y, a_Z, BlockType);
HandleRedstoneRepeater(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_PISTON:
@@ -286,7 +263,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
case E_BLOCK_REDSTONE_LAMP_OFF:
case E_BLOCK_REDSTONE_LAMP_ON:
{
HandleRedstoneLamp(a_X, dataitr->y, a_Z, BlockType);
HandleRedstoneLamp(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_DISPENSER:
@@ -305,13 +282,13 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
case E_BLOCK_DETECTOR_RAIL:
case E_BLOCK_POWERED_RAIL:
{
HandleRail(a_X, dataitr->y, a_Z, BlockType);
HandleRail(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_WOODEN_PRESSURE_PLATE:
case E_BLOCK_STONE_PRESSURE_PLATE:
{
HandlePressurePlate(a_X, dataitr->y, a_Z, BlockType);
HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
}
@@ -487,7 +464,7 @@ void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_Bl
};
// Check to see if directly beside a power source
if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
if (IsWirePowered(a_BlockX, a_BlockY, a_BlockZ))
{
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power
}
@@ -546,6 +523,7 @@ void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_Bl
// However, self not directly powered anymore, so source must have been removed,
// therefore, self must be set to meta zero
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0);
m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
return; // No need to process block power sets because self not powered
}
else
@@ -693,7 +671,7 @@ void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int
// Apparently, incrementing ticks only works reliably here, and not in SimChunk;
// With a world with lots of redstone, the repeaters simply do not delay
// I am confounded to say why. Perhaps optimisation failure.
LOGD("Incremented a repeater @ %i %i %i | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks);
LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks);
itr->a_ElapsedTicks++;
}
}
@@ -1188,6 +1166,33 @@ bool cRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, int a_Block
bool cRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ)
{
for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE)
{
return true;
}
}
for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE)
{
return true;
}
}
return false; // Source was in front of the piston's front face
}
bool cRedstoneSimulator::AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered)
{
@@ -1333,11 +1338,6 @@ void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_Block
// Don't set air, fixes some bugs (wires powering themselves)
return;
}
if ((Block == E_BLOCK_REDSTONE_WIRE) && (a_SourceBlock == E_BLOCK_REDSTONE_WIRE))
{
// Wires cannot power themselves normally, instead, the wire handler will manually set meta
return;
}
for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list
{
@@ -1354,7 +1354,6 @@ void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_Block
sPoweredBlocks RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
RC.a_SourceBlock = a_SourceBlock;
m_PoweredBlocks.push_back(RC);
}
@@ -1379,11 +1378,6 @@ void cRedstoneSimulator::SetBlockLinkedPowered(
{
return;
}
if ((a_SourceBlock == E_BLOCK_REDSTONE_WIRE) && (DestBlock == E_BLOCK_REDSTONE_WIRE))
{
// Wires cannot power another wire through a block
return;
}
for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list
{
@@ -1402,8 +1396,6 @@ void cRedstoneSimulator::SetBlockLinkedPowered(
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
RC.a_SourceBlock = a_SourceBlock;
RC.a_MiddleBlock = a_MiddleBlock;
m_LinkedPoweredBlocks.push_back(RC);
}