diff options
Diffstat (limited to 'src/Simulator/IncrementalRedstoneSimulator.cpp')
-rw-r--r-- | src/Simulator/IncrementalRedstoneSimulator.cpp | 1070 |
1 files changed, 549 insertions, 521 deletions
diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 7f320af0d..7d9cc93b2 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -3,11 +3,10 @@ #include "BlockEntities/ChestEntity.h" -typedef cItemCallback<cChestEntity> cChestCallback; - #include "Chunk.h" #include "World.h" #include "Blocks/GetHandlerCompileTimeTemplate.h" +#include "Blocks/BlockComparator.h" #include "Blocks/BlockTorch.h" #include "Blocks/BlockLever.h" #include "Blocks/BlockButton.h" @@ -25,41 +24,42 @@ typedef cItemCallback<cChestEntity> cChestCallback; -void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk, cChunk * a_OtherChunk) +void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) { - if ((a_Chunk == nullptr) || !a_Chunk->IsValid()) + AddBlock(cChunkDef::AbsoluteToRelative({ a_BlockX, a_BlockY, a_BlockZ }, a_Chunk->GetPosX(), a_Chunk->GetPosZ()), a_Chunk, nullptr); +} + + + + + +void cIncrementalRedstoneSimulator::AddBlock(const Vector3i & a_RelBlockPosition, cChunk * a_OriginalChunk, cChunk * a_NeighborChunk) +{ + if ((a_OriginalChunk == nullptr) || !a_OriginalChunk->IsValid()) { return; } - else if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + else if ((a_RelBlockPosition.y < 0) || (a_RelBlockPosition.y >= cChunkDef::Height)) { return; } - // We may be called with coordinates in a chunk that is not the first chunk parameter - // In that case, the actual chunk (which the coordinates are in), will be passed as the second parameter - // Use that Chunk pointer to get a relative position + // The relative block position is relative to the neighboring chunk should it be passed as an argument - int RelX = 0; - int RelZ = 0; BLOCKTYPE Block; NIBBLETYPE Meta; - if (a_OtherChunk != nullptr) + if (a_NeighborChunk != nullptr) { - RelX = a_BlockX - a_OtherChunk->GetPosX() * cChunkDef::Width; - RelZ = a_BlockZ - a_OtherChunk->GetPosZ() * cChunkDef::Width; - a_OtherChunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); + a_NeighborChunk->UnboundedRelGetBlock(a_RelBlockPosition.x, a_RelBlockPosition.y, a_RelBlockPosition.z, Block, Meta); - // If a_OtherChunk is passed (not nullptr), it is the chunk that had a block change, and a_Chunk will be the neighbouring chunk of that block + // If a_OtherChunk is passed (not nullptr), it is the neighbouring chunk of a_Chunk, which itself is the chunk with the block change // Because said neighbouring chunk does not know of this change but still needs to update its redstone, we set it to dirty - a_Chunk->SetIsRedstoneDirty(true); + a_NeighborChunk->SetIsRedstoneDirty(true); } else { - RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; - RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; - a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); + a_OriginalChunk->GetBlockTypeMeta(a_RelBlockPosition.x, a_RelBlockPosition.y, a_RelBlockPosition.z, Block, Meta); } // 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 @@ -77,68 +77,61 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, ) ) { - SetSourceUnpowered(RelX, a_BlockY, RelZ, a_OtherChunk != nullptr ? a_OtherChunk : a_Chunk); + SetSourceUnpowered(a_RelBlockPosition.x, a_RelBlockPosition.y, a_RelBlockPosition.z, a_NeighborChunk != nullptr ? a_NeighborChunk : a_OriginalChunk); } if (!IsViableMiddleBlock(Block)) { - SetInvalidMiddleBlock(RelX, a_BlockY, RelZ, a_OtherChunk != nullptr ? a_OtherChunk : a_Chunk); + SetInvalidMiddleBlock(a_RelBlockPosition.x, a_RelBlockPosition.y, a_RelBlockPosition.z, a_NeighborChunk != nullptr ? a_NeighborChunk : a_OriginalChunk); } - auto & SimulatedPlayerToggleableBlocks = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_SimulatedPlayerToggleableBlocks; - SimulatedPlayerToggleableBlocks.erase(std::remove_if(SimulatedPlayerToggleableBlocks.begin(), SimulatedPlayerToggleableBlocks.end(), [RelX, a_BlockY, RelZ, Block, this](const sSimulatedPlayerToggleableList & itr) - { - return itr.a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)) && !IsAllowedBlock(Block); - } - ), SimulatedPlayerToggleableBlocks.end()); - - - auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList; - RepeatersDelayList.erase(std::remove_if(RepeatersDelayList.begin(), RepeatersDelayList.end(), [RelX, a_BlockY, RelZ, Block](const sRepeatersDelayList & itr) - { - return itr.a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)) && (Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF); - } - ), RepeatersDelayList.end()); - - if (a_OtherChunk != nullptr) + if (a_NeighborChunk != nullptr) { // DO NOT touch our chunk's data structure if we are being called with coordinates from another chunk - this one caused me massive grief :P return; } + auto RedstoneSimulatorData = static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_OriginalChunk->GetRedstoneSimulatorData()); - cCoordWithBlockAndBoolVector & RedstoneSimulatorChunkData = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_ChunkData; - for (auto & itr : RedstoneSimulatorChunkData) + auto & SimulatedPlayerToggleableBlocks = RedstoneSimulatorData->m_SimulatedPlayerToggleableBlocks; + if (DoesIgnorePlayerToggle(Block)) { - if ((itr.x == RelX) && (itr.y == a_BlockY) && (itr.z == RelZ)) // We are at an entry matching the current (changed) block - { - if (!IsAllowedBlock(Block)) - { - itr.DataTwo = true; // The new blocktype is not redstone; it must be queued to be removed from this list - } - else - { - itr.DataTwo = false; - itr.Data = Block; // Update block information - } - return; - } + // Initialise the toggleable blocks list so that trapdoors etc. aren't reset on restart (#1887) + SimulatedPlayerToggleableBlocks.emplace( + a_RelBlockPosition, + AreCoordsDirectlyPowered(a_RelBlockPosition.x, a_RelBlockPosition.y, a_RelBlockPosition.z, a_OriginalChunk) || AreCoordsLinkedPowered(a_RelBlockPosition.x, a_RelBlockPosition.y, a_RelBlockPosition.z, a_OriginalChunk) + ); // This map won't insert if key already present, so no need to check + } + else + { + SimulatedPlayerToggleableBlocks.erase(a_RelBlockPosition); } + if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF)) + { + RedstoneSimulatorData->m_RepeatersDelayList.erase(a_RelBlockPosition); + } + + auto & RedstoneSimulatorChunkData = RedstoneSimulatorData->m_ChunkData; + auto Iterator = RedstoneSimulatorChunkData.find(a_RelBlockPosition); if (!IsAllowedBlock(Block)) { + if (Iterator != RedstoneSimulatorChunkData.end()) + { + Iterator->second.second = true; // The new blocktype is not redstone; it must be queued to be removed from this list + } return; } - - cCoordWithBlockAndBoolVector & QueuedData = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData; - for (const auto & itr : QueuedData) + else { - if ((itr.x == RelX) && (itr.y == a_BlockY) && (itr.z == RelZ)) + if (Iterator != RedstoneSimulatorChunkData.end()) { - // Can't have duplicates in here either, in case something adds the block again before the structure can written to the main chunk data + Iterator->second.second = false; // De-schedule removal from list + Iterator->second.first = Block; // Update block information return; } } - QueuedData.emplace_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false)); + + RedstoneSimulatorChunkData.emplace(a_RelBlockPosition, std::make_pair(Block, false)); } @@ -147,25 +140,17 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { - m_RedstoneSimulatorChunkData = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); + m_RedstoneSimulatorChunkData = static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_Chunk->GetRedstoneSimulatorData()); if (m_RedstoneSimulatorChunkData == nullptr) { m_RedstoneSimulatorChunkData = new cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData(); a_Chunk->SetRedstoneSimulatorData(m_RedstoneSimulatorChunkData); } - if (m_RedstoneSimulatorChunkData->m_ChunkData.empty() && ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_QueuedChunkData.empty()) + if (m_RedstoneSimulatorChunkData->m_ChunkData.empty()) { return; } - m_RedstoneSimulatorChunkData->m_ChunkData.insert( - m_RedstoneSimulatorChunkData->m_ChunkData.end(), - m_RedstoneSimulatorChunkData->m_QueuedChunkData.begin(), - m_RedstoneSimulatorChunkData->m_QueuedChunkData.end() - ); - - m_RedstoneSimulatorChunkData->m_QueuedChunkData.clear(); - m_PoweredBlocks = &m_RedstoneSimulatorChunkData->m_PoweredBlocks; m_RepeatersDelayList = &m_RedstoneSimulatorChunkData->m_RepeatersDelayList; m_SimulatedPlayerToggleableBlocks = &m_RedstoneSimulatorChunkData->m_SimulatedPlayerToggleableBlocks; @@ -185,24 +170,25 @@ void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt for (auto dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.begin(); dataitr != m_RedstoneSimulatorChunkData->m_ChunkData.end();) { - if (dataitr->DataTwo) + if (dataitr->second.second) { + // Removal was scheduled - do so dataitr = m_RedstoneSimulatorChunkData->m_ChunkData.erase(dataitr); continue; } - switch (dataitr->Data) + switch (dataitr->second.first) // Call the appropriate simulator for the entry's block type { - case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRIPWIRE: HandleTripwire(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRIPWIRE_HOOK: HandleTripwireHook(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_TRIPWIRE: HandleTripwire(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_TRIPWIRE_HOOK: HandleTripwireHook(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; case E_BLOCK_WOODEN_PRESSURE_PLATE: case E_BLOCK_STONE_PRESSURE_PLATE: case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: { - HandlePressurePlate(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + HandlePressurePlate(dataitr->first.x, dataitr->first.y, dataitr->first.z, dataitr->second.first); break; } default: break; @@ -210,23 +196,23 @@ void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt if (ShouldUpdateSimulateOnceBlocks) { - switch (dataitr->Data) + switch (dataitr->second.first) { - case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_LEVER: HandleRedstoneLever(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TNT: HandleTNT(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_IRON_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break; - case E_BLOCK_TRAPPED_CHEST: HandleTrappedChest(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_LEVER: HandleRedstoneLever(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_TNT: HandleTNT(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_IRON_TRAPDOOR: HandleTrapdoor(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; + case E_BLOCK_TRAPPED_CHEST: HandleTrappedChest(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; case E_BLOCK_ACTIVATOR_RAIL: case E_BLOCK_DETECTOR_RAIL: case E_BLOCK_POWERED_RAIL: { - HandleRail(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + HandleRail(dataitr->first.x, dataitr->first.y, dataitr->first.z, dataitr->second.first); break; } case E_BLOCK_ACACIA_DOOR: @@ -237,7 +223,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt case E_BLOCK_WOODEN_DOOR: case E_BLOCK_IRON_DOOR: { - HandleDoor(dataitr->x, dataitr->y, dataitr->z); + HandleDoor(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; } case E_BLOCK_ACACIA_FENCE_GATE: @@ -247,43 +233,49 @@ void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt case E_BLOCK_JUNGLE_FENCE_GATE: case E_BLOCK_SPRUCE_FENCE_GATE: { - HandleFenceGate(dataitr->x, dataitr->y, dataitr->z); + HandleFenceGate(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; } case E_BLOCK_REDSTONE_LAMP_OFF: case E_BLOCK_REDSTONE_LAMP_ON: { - HandleRedstoneLamp(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + HandleRedstoneLamp(dataitr->first.x, dataitr->first.y, dataitr->first.z, dataitr->second.first); break; } case E_BLOCK_DISPENSER: case E_BLOCK_DROPPER: { - HandleDropSpenser(dataitr->x, dataitr->y, dataitr->z); + HandleDropSpenser(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; } case E_BLOCK_PISTON: case E_BLOCK_STICKY_PISTON: { - HandlePiston(dataitr->x, dataitr->y, dataitr->z); + HandlePiston(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; } case E_BLOCK_REDSTONE_REPEATER_OFF: case E_BLOCK_REDSTONE_REPEATER_ON: { - HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + HandleRedstoneRepeater(dataitr->first.x, dataitr->first.y, dataitr->first.z, dataitr->second.first); break; } case E_BLOCK_REDSTONE_TORCH_OFF: case E_BLOCK_REDSTONE_TORCH_ON: { - HandleRedstoneTorch(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + HandleRedstoneTorch(dataitr->first.x, dataitr->first.y, dataitr->first.z, dataitr->second.first); break; } case E_BLOCK_STONE_BUTTON: case E_BLOCK_WOODEN_BUTTON: { - HandleRedstoneButton(dataitr->x, dataitr->y, dataitr->z); + HandleRedstoneButton(dataitr->first.x, dataitr->first.y, dataitr->first.z); + break; + } + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_INACTIVE_COMPARATOR: + { + HandleRedstoneComparator(dataitr->first.x, dataitr->first.y, dataitr->first.z); break; } default: break; @@ -299,23 +291,14 @@ void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt void cIncrementalRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) { - if (AreCoordsOnChunkBoundary(a_BlockX, a_BlockY, a_BlockZ)) - { - // On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk) - AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); + auto CurrentChunkRelative = cChunkDef::AbsoluteToRelative({a_BlockX, a_BlockY, a_BlockZ}, a_Chunk->GetPosX(), a_Chunk->GetPosZ()); + AddBlock(CurrentChunkRelative, a_Chunk); // Alert the current chunk which the block is present in - // Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position - // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordinates are in and +- 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk); - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk); - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk); - RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 2), a_Chunk); - - return; + for (const auto & BoundaryChunk : GetAdjacentChunks(CurrentChunkRelative, a_Chunk)) + { + // On a chunk boundary, alert all neighbouring chunks which may have a connection with this block + AddBlock(cChunkDef::AbsoluteToRelative({a_BlockX, a_BlockY, a_BlockZ}, BoundaryChunk->GetPosX(), BoundaryChunk->GetPosZ()), a_Chunk, BoundaryChunk); } - - // Not on boundary, just alert this chunk for speed - AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); } @@ -503,152 +486,278 @@ void cIncrementalRedstoneSimulator::HandleRedstoneButton(int a_RelBlockX, int a_ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { - static const struct // Define which directions the wire can receive power from - { - int x, y, z; - } gCrossCoords[] = + static const eBlockFace BlockFaceOffsets[] = { - { 1, 0, 0 }, /* Wires on same level start */ - { -1, 0, 0 }, - { 0, 0, 1 }, - { 0, 0, -1 }, /* Wires on same level stop */ - { 1, 1, 0 }, /* Wires one higher, surrounding self start */ - { -1, 1, 0 }, - { 0, 1, 1 }, - { 0, 1, -1 }, /* Wires one higher, surrounding self stop */ - { 1, -1, 0 }, /* Wires one lower, surrounding self start */ - { -1, -1, 0 }, - { 0, -1, 1 }, - { 0, -1, -1 }, /* Wires one lower, surrounding self stop */ + BLOCK_FACE_XM, + BLOCK_FACE_XP, + BLOCK_FACE_ZM, + BLOCK_FACE_ZP }; - static const struct // Define which directions the wire will check for repeater prescence + static const Vector3i VectorOffsets[] = { - int x, y, z; - } gSideCoords[] = - { - { 1, 0, 0 }, { -1, 0, 0 }, - { 0, 0, 1 }, + { 1, 0, 0 }, { 0, 0, -1 }, - { 0, 1, 0 }, + { 0, 0, 1 } }; + auto RelBlock = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + // Check to see if directly beside a power source - unsigned char MyPower; - if (!IsWirePowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower)) + unsigned char MyPower = IsWirePowered(RelBlock, m_Chunk); + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + if (MyPower == 0) { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); return; } - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - - if (MyPower < 1) + if (MyPower == MAX_POWER_LEVEL) + { + std::vector<std::pair<Vector3i, cChunk *>> PotentialWireList = { std::make_pair(RelBlock, m_Chunk) }; + while (!PotentialWireList.empty()) + { + auto Current = PotentialWireList.back(); + PotentialWireList.pop_back(); + FindAndPowerBorderingWires(PotentialWireList, Current.first, Current.second); + } + } + else if (MyPower == 1) { return; } + // Wire still powered, power blocks beneath and in direction of facing MyPower--; - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power + SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, MyPower); + + int BorderingMechanismCount = 0; + bool RepeaterPresent = false; + Vector3i OffsetToPower; + + for (const auto & Offset : VectorOffsets) { - if ((i >= 4) && (i <= 7)) // If we are currently checking for wire surrounding ourself one block above... + BLOCKTYPE Block; + Vector3i AdjustedOffset = RelBlock + Offset; + if (m_Chunk->UnboundedRelGetBlockType(AdjustedOffset.x, AdjustedOffset.y, AdjustedOffset.z, Block)) { - BLOCKTYPE Type = 0; - if (a_RelBlockY >= cChunkDef::Height - 1) + switch (Block) { - continue; - } - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ, Type)) - { - continue; - } - if (cBlockInfo::IsSolid(Type)) // If there is something solid above us (wire cut off)... - { - continue; // We don't receive power from that wire + case E_BLOCK_REDSTONE_REPEATER_OFF: + { + BorderingMechanismCount++; + if (!RepeaterPresent) + { + // TODO: only if wire is actually connected to repeater (repeater facing right way) + RepeaterPresent = true; + OffsetToPower = { -Offset.x, Offset.y, -Offset.z }; // Negate to obtain offset in opposite direction since wire powers that way + } + SetBlockPowered(AdjustedOffset, RelBlock, MyPower); + } + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_WIRE: + { + BorderingMechanismCount++; + if (!RepeaterPresent) + { + OffsetToPower = { -Offset.x, Offset.y, -Offset.z }; + } + } + default: break; } } - else if ((i >= 8) && (i <= 11)) // See above, but this is for wire below us + } + + if (BorderingMechanismCount == 0) + { + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + for (const auto & BlockFaceOffset : BlockFaceOffsets) { - BLOCKTYPE Type = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY, a_RelBlockZ + gCrossCoords[i].z, Type)) - { - continue; - } - if (cBlockInfo::IsSolid(Type)) - { - continue; - } + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BlockFaceOffset, MyPower); } + } + else if (BorderingMechanismCount == 1) + { + eBlockFace Face = BlockFaceOffsets[std::distance(VectorOffsets, std::find(VectorOffsets, VectorOffsets + ARRAYCOUNT(VectorOffsets), OffsetToPower))]; + SetBlockPowered(RelBlock + OffsetToPower, RelBlock, MyPower); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Face, MyPower); + } +} - BLOCKTYPE Type = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, Type)) + + + + +void cIncrementalRedstoneSimulator::FindAndPowerBorderingWires(std::vector<std::pair<Vector3i, cChunk *>> & a_PotentialWireList, const Vector3i & a_EntryRelBlockPosition, cChunk * a_EntryChunk) +{ + static const Vector3i LevelOffsets[] = // Wires on same level + { + { 1, 0, 0 }, + { -1, 0, 0 }, + { 0, 0, 1 }, + { 0, 0, -1 }, + }; + static const Vector3i HigherOffsets[] = // Wires one higher, surrounding self + { + { 1, 1, 0 }, + { -1, 1, 0 }, + { 0, 1, 1 }, + { 0, 1, -1 }, + }; + static const Vector3i LowerOffsets[] = // Wires one lower, surrounding self + { + { 1, -1, 0 }, + { -1, -1, 0 }, + { 0, -1, 1 }, + { 0, -1, -1 }, + }; + + for (auto Offset : LevelOffsets) + { + auto AdjustedPos = a_EntryRelBlockPosition + Offset; + auto Neighbour = a_EntryChunk->GetRelNeighborChunkAdjustCoords(AdjustedPos.x, AdjustedPos.z); + auto MyPower = IsWirePowered(a_EntryRelBlockPosition, a_EntryChunk); + + if ((Neighbour == nullptr) || !Neighbour->IsValid()) { - continue; + return; } - if (Type == E_BLOCK_REDSTONE_WIRE) + + if ((Neighbour->GetBlock(AdjustedPos) == E_BLOCK_REDSTONE_WIRE) && (MyPower > 1) && (MyPower > IsWirePowered(AdjustedPos, Neighbour))) { - SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + PowerBorderingWires(a_PotentialWireList, a_EntryRelBlockPosition, a_EntryChunk, AdjustedPos, Neighbour, MyPower); } } - for (size_t i = 0; i < ARRAYCOUNT(gSideCoords); i++) // Look for repeaters immediately surrounding self and try to power them + for (auto Offset : HigherOffsets) { - BLOCKTYPE Type = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, Type)) + auto AdjustedPos = a_EntryRelBlockPosition + Offset; + auto Neighbour = a_EntryChunk->GetRelNeighborChunkAdjustCoords(AdjustedPos.x, AdjustedPos.z); + auto MyPower = IsWirePowered(a_EntryRelBlockPosition, a_EntryChunk); + + if ((Neighbour == nullptr) || !Neighbour->IsValid()) { - continue; + return; } - if (Type == E_BLOCK_REDSTONE_REPEATER_OFF) + + if ( + (Neighbour->GetBlock(AdjustedPos) == E_BLOCK_REDSTONE_WIRE) && + (!cBlockInfo::FullyOccupiesVoxel(a_EntryChunk->GetBlock(a_EntryRelBlockPosition.x, a_EntryRelBlockPosition.y + 1, a_EntryRelBlockPosition.z))) && + (MyPower > 1) && (MyPower > IsWirePowered(AdjustedPos, Neighbour))) { - SetBlockPowered(a_RelBlockX + gSideCoords[i].x, a_RelBlockY + gSideCoords[i].y, a_RelBlockZ + gSideCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + PowerBorderingWires(a_PotentialWireList, a_EntryRelBlockPosition, a_EntryChunk, AdjustedPos, Neighbour, MyPower); } } - // Wire still powered, power blocks beneath - SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, MyPower); - - switch (GetWireDirection(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) + for (auto Offset : LowerOffsets) { - case REDSTONE_NONE: - { - SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); + auto AdjustedPos = a_EntryRelBlockPosition + Offset; + auto Neighbour = a_EntryChunk->GetRelNeighborChunkAdjustCoords(AdjustedPos.x, AdjustedPos.z); + auto MyPower = IsWirePowered(a_EntryRelBlockPosition, a_EntryChunk); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower); - break; - } - case REDSTONE_X_POS: + if ((Neighbour == nullptr) || !Neighbour->IsValid()) { - SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP, MyPower); - break; + return; } - case REDSTONE_X_NEG: + + if ( + (Neighbour->GetBlock(AdjustedPos) == E_BLOCK_REDSTONE_WIRE) && + (!cBlockInfo::FullyOccupiesVoxel(Neighbour->GetBlock(AdjustedPos.x, AdjustedPos.y + 1, AdjustedPos.z))) && + (MyPower > 1) && (MyPower > IsWirePowered(AdjustedPos, Neighbour))) { - SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM, MyPower); - break; + PowerBorderingWires(a_PotentialWireList, a_EntryRelBlockPosition, a_EntryChunk, AdjustedPos, Neighbour, MyPower); } - case REDSTONE_Z_POS: + } +} + + + + + +void cIncrementalRedstoneSimulator::PowerBorderingWires(std::vector<std::pair<Vector3i, cChunk *>> & a_PotentialWireList, const Vector3i & a_EntryRelSourcePosition, cChunk * a_EntryChunk, const Vector3i & a_AdjustedPos, cChunk * a_NeighbourChunk, unsigned char a_MyPower) +{ + auto SourcePos = a_EntryRelSourcePosition + Vector3i((a_EntryChunk->GetPosX() - a_NeighbourChunk->GetPosX()) * cChunkDef::Width, 0, (a_EntryChunk->GetPosZ() - a_NeighbourChunk->GetPosZ()) * cChunkDef::Width); + auto & PoweredBlocks = static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_NeighbourChunk->GetRedstoneSimulatorData())->m_PoweredBlocks; // We need to insert the value into the chunk who owns the block position + auto Position = std::find_if(PoweredBlocks.begin(), PoweredBlocks.end(), [a_AdjustedPos, SourcePos, a_MyPower](const sPoweredBlocks & itr) { return ((itr.m_BlockPos == a_AdjustedPos) && (itr.m_SourcePos == SourcePos)); }); + if (Position != PoweredBlocks.end()) + { + Position->m_PowerLevel = a_MyPower - 1; + } + else + { + PoweredBlocks.emplace_back(a_AdjustedPos, SourcePos, a_MyPower - 1); + + a_NeighbourChunk->SetIsRedstoneDirty(true); + m_Chunk->SetIsRedstoneDirty(true); + } + + a_NeighbourChunk->SetMeta(a_AdjustedPos.x, a_AdjustedPos.y, a_AdjustedPos.z, a_MyPower - 1); + a_PotentialWireList.emplace_back(std::make_pair(a_AdjustedPos, a_NeighbourChunk)); +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneComparator(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + auto Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + auto HighestSidePower = std::max(IsWirePowered(AdjustRelativeCoords(cBlockComparatorHandler::GetSideCoordinate(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta & 0x3, false)), m_Chunk), IsWirePowered(AdjustRelativeCoords(cBlockComparatorHandler::GetSideCoordinate(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta & 0x3, true)), m_Chunk)); + auto FrontCoordinate = AdjustRelativeCoords(cBlockComparatorHandler::GetFrontCoordinate(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta & 0x3)); + unsigned char Power = 0; + + class cContainerCallback : public cBlockEntityCallback + { + public: + cContainerCallback() : m_SignalStrength(0) { - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP, MyPower); - break; } - case REDSTONE_Z_NEG: + + virtual bool Item(cBlockEntity * a_BlockEntity) override { - SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ, MyPower); - SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM, MyPower); - break; + auto & Contents = static_cast<cBlockEntityWithItems *>(a_BlockEntity)->GetContents(); + float Fullness = 0; // Is a floating-point type to allow later calculation to produce a non-truncated value + for (int Slot = 0; Slot != Contents.GetNumSlots(); ++Slot) + { + Fullness += Contents.GetSlot(Slot).m_ItemCount / Contents.GetSlot(Slot).GetMaxStackSize(); + } + + m_SignalStrength = static_cast<unsigned char>(1 + (Fullness / Contents.GetNumSlots()) * 14); + return false; } + + unsigned char m_SignalStrength; + } CCB; + + auto AbsoluteComparatorCoords = cChunkDef::RelativeToAbsolute(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ), m_Chunk->GetPosX(), m_Chunk->GetPosZ()); + auto AbsoluteRearCoords = cBlockComparatorHandler::GetRearCoordinate(AbsoluteComparatorCoords.x, AbsoluteComparatorCoords.y, AbsoluteComparatorCoords.z, Meta & 0x3); + + m_Chunk->DoWithBlockEntityAt(AbsoluteRearCoords.x, AbsoluteRearCoords.y, AbsoluteRearCoords.z, CCB); + auto RearPower = std::max(CCB.m_SignalStrength, IsWirePowered(AdjustRelativeCoords(cBlockComparatorHandler::GetRearCoordinate(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta & 0x3)), m_Chunk)); + + if ((Meta & 0x4) == 0x4) + { + // Subtraction mode + Power = std::max(static_cast<unsigned char>(RearPower - HighestSidePower), std::numeric_limits<unsigned char>::min()); + } + else + { + // Comparison mode + Power = (std::max(HighestSidePower, RearPower) == HighestSidePower) ? 0 : RearPower; + } + + if (Power > 0) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0x8); + SetBlockPowered(FrontCoordinate, Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ), Power); + } + else + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); } } @@ -680,19 +789,50 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_RelBlockX, int */ // Create a variable holding my meta to avoid multiple lookups. - NIBBLETYPE a_Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); bool IsOn = (a_MyState == E_BLOCK_REDSTONE_REPEATER_ON); - if (!IsRepeaterLocked(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta)) // If we're locked, change nothing. Otherwise: + if (!IsRepeaterLocked(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta)) // If we're locked, change nothing. Otherwise: { - bool IsSelfPowered = IsRepeaterPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta); + bool IsSelfPowered = IsRepeaterPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta); if (IsSelfPowered && !IsOn) // Queue a power change if powered, but not on and not locked. { - QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, true); + QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta, true); } else if (!IsSelfPowered && IsOn) // Queue a power change if unpowered, on, and not locked. { - QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_Meta, false); + QueueRepeaterPowerChange(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Meta, false); + } + } + + if (IsOn) + { + switch (Meta & 0x3) // We only want the direction (bottom) bits + { + case 0x0: + { + SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZM); + break; + } + case 0x1: + { + SetBlockPowered(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XP); + break; + } + case 0x2: + { + SetBlockPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_ZP); + break; + } + case 0x3: + { + SetBlockPowered(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_XM); + break; + } } } } @@ -702,60 +842,29 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeaterDelays() { for (auto itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end();) { - if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? + if (itr->second.a_ElapsedTicks >= itr->second.a_DelayTicks) // Has the elapsed ticks reached the target ticks? { - int RelBlockX = itr->a_RelBlockPos.x; - int RelBlockY = itr->a_RelBlockPos.y; - int RelBlockZ = itr->a_RelBlockPos.z; BLOCKTYPE Block; NIBBLETYPE Meta; - m_Chunk->GetBlockTypeMeta(RelBlockX, RelBlockY, RelBlockZ, Block, Meta); - if (itr->ShouldPowerOn) + m_Chunk->GetBlockTypeMeta(itr->first.x, itr->first.y, itr->first.z, Block, Meta); + if (itr->second.ShouldPowerOn) { if (Block != E_BLOCK_REDSTONE_REPEATER_ON) // For performance { - m_Chunk->SetBlock(itr->a_RelBlockPos, E_BLOCK_REDSTONE_REPEATER_ON, Meta); - } - - switch (Meta & 0x3) // We only want the direction (bottom) bits - { - case 0x0: - { - SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ - 1, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZM); - break; - } - case 0x1: - { - SetBlockPowered(RelBlockX + 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XP); - break; - } - case 0x2: - { - SetBlockPowered(RelBlockX, RelBlockY, RelBlockZ + 1, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_ZP); - break; - } - case 0x3: - { - SetBlockPowered(RelBlockX - 1, RelBlockY, RelBlockZ, RelBlockX, RelBlockY, RelBlockZ); - SetDirectionLinkedPowered(RelBlockX, RelBlockY, RelBlockZ, BLOCK_FACE_XM); - break; - } + m_Chunk->SetBlock(itr->first, E_BLOCK_REDSTONE_REPEATER_ON, Meta); } } else if (Block != E_BLOCK_REDSTONE_REPEATER_OFF) { - m_Chunk->SetBlock(RelBlockX, RelBlockY, RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, Meta); + m_Chunk->SetBlock(itr->first.x, itr->first.y, itr->first.z, E_BLOCK_REDSTONE_REPEATER_OFF, Meta); } itr = m_RepeatersDelayList->erase(itr); } else { - LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); - itr->a_ElapsedTicks++; - itr++; + LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->first.x, itr->first.y, itr->first.z, itr->second.a_ElapsedTicks, itr->second.a_DelayTicks); + itr->second.a_ElapsedTicks++; + ++itr; } } } @@ -1017,19 +1126,19 @@ void cIncrementalRedstoneSimulator::HandleDaylightSensor(int a_RelBlockX, int a_ int ChunkX, ChunkZ; cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); - if (!this->m_World.IsChunkLighted(ChunkX, ChunkZ)) + if (!m_World.IsChunkLighted(ChunkX, ChunkZ)) { - this->m_World.QueueLightChunk(ChunkX, ChunkZ); + m_World.QueueLightChunk(ChunkX, ChunkZ); } else { - if (m_Chunk->GetTimeAlteredLight(this->m_World.GetBlockSkyLight(BlockX, a_RelBlockY + 1, BlockZ)) > 8) + if (m_Chunk->GetTimeAlteredLight(m_Chunk->GetSkyLight(a_RelBlockX, a_RelBlockY + 1, a_RelBlockZ)) > 8) { SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); } else { - WakeUp(BlockX, a_RelBlockY, BlockZ, m_Chunk); + SetSourceUnpowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk); } } } @@ -1452,30 +1561,18 @@ bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_RelBlockX, in { // Torches want to access neighbour's data when on a wall, hence the extra chunk parameter - for (const auto & itr : ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks) // Check powered list - { - if (itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - return true; - } - } - return false; + const auto & Data = static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks; + return std::find_if(Data.begin(), Data.end(), [a_RelBlockX, a_RelBlockY, a_RelBlockZ](const sPoweredBlocks & itr) { return itr.m_BlockPos == Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); }) != Data.end(); } -bool cIncrementalRedstoneSimulator::AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +bool cIncrementalRedstoneSimulator::AreCoordsLinkedPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, cChunk * a_Chunk) { - for (const auto & itr : *m_LinkedPoweredBlocks) // Check linked powered list - { - if (itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - return true; - } - } - return false; + const auto & Data = static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks; + return std::find_if(Data.begin(), Data.end(), [a_RelBlockX, a_RelBlockY, a_RelBlockZ](const sLinkedPoweredBlocks & itr) { return itr.a_BlockPos == Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); }) != Data.end(); } @@ -1488,7 +1585,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel for (const auto & itr : *m_PoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + if (!itr.m_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } @@ -1498,7 +1595,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel case 0x0: { // Flip the coords to check the back of the repeater - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) + if (itr.m_SourcePos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1))) { return true; } @@ -1506,7 +1603,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel } case 0x1: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) + if (itr.m_SourcePos.Equals(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ))) { return true; } @@ -1514,7 +1611,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel } case 0x2: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) + if (itr.m_SourcePos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1))) { return true; } @@ -1522,7 +1619,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel } case 0x3: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) + if (itr.m_SourcePos.Equals(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ))) { return true; } @@ -1542,7 +1639,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel { case 0x0: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) + if (itr.a_MiddlePos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1))) { return true; } @@ -1550,7 +1647,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel } case 0x1: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) + if (itr.a_MiddlePos.Equals(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ))) { return true; } @@ -1558,7 +1655,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel } case 0x2: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) + if (itr.a_MiddlePos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1))) { return true; } @@ -1566,7 +1663,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel } case 0x3: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) + if (itr.a_MiddlePos.Equals(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ))) { return true; } @@ -1667,7 +1764,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl for (const auto & itr : *m_PoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + if (!itr.m_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } @@ -1675,7 +1772,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl int X = a_RelBlockX, Z = a_RelBlockZ; AddFaceDirection(X, a_RelBlockY, Z, Face); - if (!itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(X, a_RelBlockY, Z)))) + if (!itr.m_SourcePos.Equals(AdjustRelativeCoords(Vector3i(X, a_RelBlockY, Z)))) { return true; } @@ -1702,35 +1799,35 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl -bool cIncrementalRedstoneSimulator::IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel) +unsigned char cIncrementalRedstoneSimulator::IsWirePowered(Vector3i a_RelBlockPosition, cChunk * a_Chunk) { - a_PowerLevel = 0; + unsigned char PowerLevel = 0; - for (const auto & itr : *m_PoweredBlocks) // Check powered list + for (const auto & itr : static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_Chunk->GetRedstoneSimulatorData())->m_PoweredBlocks) // Check powered list { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + if (itr.m_BlockPos != a_RelBlockPosition) { continue; } - a_PowerLevel = std::max(itr.a_PowerLevel, a_PowerLevel); // Get the highest power level (a_PowerLevel is initialised already and there CAN be multiple levels for one block) + PowerLevel = std::max(itr.m_PowerLevel, PowerLevel); // Get the highest power level (a_PowerLevel is initialised already and there CAN be multiple levels for one block) } - for (const auto & itr : *m_LinkedPoweredBlocks) // Check linked powered list + for (const auto & itr : static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_Chunk->GetRedstoneSimulatorData())->m_LinkedBlocks) // Check linked powered list { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + if (itr.a_BlockPos != a_RelBlockPosition) { continue; } BLOCKTYPE Type = E_BLOCK_AIR; - if (!m_Chunk->UnboundedRelGetBlockType(itr.a_SourcePos.x, itr.a_SourcePos.y, itr.a_SourcePos.z, Type) || (Type == E_BLOCK_REDSTONE_WIRE)) + if (!a_Chunk->UnboundedRelGetBlockType(itr.a_SourcePos.x, itr.a_SourcePos.y, itr.a_SourcePos.z, Type) || (Type == E_BLOCK_REDSTONE_WIRE)) { continue; } - a_PowerLevel = std::max(itr.a_PowerLevel, a_PowerLevel); + PowerLevel = std::max(itr.a_PowerLevel, PowerLevel); } - return (a_PowerLevel != 0); // Answer the inital question: is the wire powered? + return PowerLevel; } @@ -1741,9 +1838,9 @@ bool cIncrementalRedstoneSimulator::AreCoordsSimulated(int a_RelBlockX, int a_Re { for (const auto & itr : *m_SimulatedPlayerToggleableBlocks) { - if (itr.a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + if (itr.first.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { - if (itr.WasLastStatePowered != IsCurrentStatePowered) // Was the last power state different to the current? + if (itr.second != IsCurrentStatePowered) // Was the last power state different to the current? { return false; // It was, coordinates are no longer simulated } @@ -1869,22 +1966,19 @@ void cIncrementalRedstoneSimulator::SetDirectionLinkedPowered(int a_RelBlockX, i void cIncrementalRedstoneSimulator::SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel) { - static const struct - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0 }, - { -1, 0, 0 }, - { 0, 0, 1 }, - { 0, 0, -1 }, - { 0, 1, 0 }, - { 0, -1, 0 } + static const Vector3i Offsets[] = + { + {1, 0, 0}, + { -1, 0, 0}, + {0, 0, 1}, + {0, 0, -1}, + {0, 1, 0}, + {0, -1, 0} }; - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through struct to power all directions + for (auto Offset : Offsets) // Loop through struct to power all directions { - SetBlockPowered(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z, a_RelBlockX, a_RelBlockY, a_RelBlockZ, a_PowerLevel); + SetBlockPowered(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ) + Offset, Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ), a_PowerLevel); } } @@ -1892,62 +1986,33 @@ void cIncrementalRedstoneSimulator::SetAllDirsAsPowered(int a_RelBlockX, int a_R -void cIncrementalRedstoneSimulator::SetBlockPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, unsigned char a_PowerLevel) +void cIncrementalRedstoneSimulator::SetBlockPowered(Vector3i a_RelBlockPosition, Vector3i a_RelSourcePosition, unsigned char a_PowerLevel) { - cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); // Adjust coordinates for the later call using these values + cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockPosition.x, a_RelBlockPosition.z); // Adjust coordinates for the later call using these values if ((Neighbour == nullptr) || !Neighbour->IsValid()) { return; } + a_RelSourcePosition.x += (m_Chunk->GetPosX() - Neighbour->GetPosX()) * cChunkDef::Width; + a_RelSourcePosition.z += (m_Chunk->GetPosZ() - Neighbour->GetPosZ()) * cChunkDef::Width; - auto & Powered = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_PoweredBlocks; // We need to insert the value into the chunk who owns the block position - for (auto itr = Powered.begin(); itr != Powered.end(); ++itr) + auto & Powered = static_cast<cIncrementalRedstoneSimulatorChunkData *>(Neighbour->GetRedstoneSimulatorData())->m_PoweredBlocks; // We need to insert the value into the chunk who owns the block position + for (auto & itr : Powered) { - if ( - itr->a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) && - itr->a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) - ) + if ((itr.m_BlockPos == a_RelBlockPosition) && (itr.m_SourcePos == a_RelSourcePosition)) { - // Check for duplicates, update power level, don't add a new listing - itr->a_PowerLevel = a_PowerLevel; - return; - } - } - - // No need to get neighbouring chunk as we can guarantee that when something is powering us, the entry will be in our chunk - for (auto itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) - { - if ( - itr->a_BlockPos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) && - itr->a_SourcePos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) && - (m_Chunk->GetBlock(a_RelSourceX, a_RelSourceY, a_RelSourceZ) == E_BLOCK_REDSTONE_WIRE) - ) - { - BLOCKTYPE Block; - NIBBLETYPE Meta; - Neighbour->GetBlockTypeMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Block, Meta); - - if (Block == E_BLOCK_REDSTONE_WIRE) + if (itr.m_PowerLevel != a_PowerLevel) { - if (Meta < a_PowerLevel) - { - m_PoweredBlocks->erase(itr); // Powering source with higher power level, allow it - break; - } - else - { - // Powered wires try to power their source - don't let them! - return; - } + // Update power level, don't add a new listing + Neighbour->SetIsRedstoneDirty(true); + m_Chunk->SetIsRedstoneDirty(true); + itr.m_PowerLevel = a_PowerLevel; } + return; } } - sPoweredBlocks RC; - RC.a_BlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - RC.a_SourcePos = Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ); - RC.a_PowerLevel = a_PowerLevel; - Powered.emplace_back(RC); + Powered.emplace_back(a_RelBlockPosition, a_RelSourcePosition, a_PowerLevel); Neighbour->SetIsRedstoneDirty(true); m_Chunk->SetIsRedstoneDirty(true); } @@ -1969,13 +2034,16 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( } cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); - m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelMiddleX, a_RelMiddleZ); if ((Neighbour == nullptr) || !Neighbour->IsValid()) { return; } + a_RelMiddleX += (m_Chunk->GetPosX() - Neighbour->GetPosX()) * cChunkDef::Width; + a_RelMiddleZ += (m_Chunk->GetPosZ() - Neighbour->GetPosZ()) * cChunkDef::Width; + a_RelSourceX += (m_Chunk->GetPosX() - Neighbour->GetPosX()) * cChunkDef::Width; + a_RelSourceZ += (m_Chunk->GetPosZ() - Neighbour->GetPosZ()) * cChunkDef::Width; - auto & Linked = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)Neighbour->GetRedstoneSimulatorData())->m_LinkedBlocks; + auto & Linked = static_cast<cIncrementalRedstoneSimulatorChunkData *>(Neighbour->GetRedstoneSimulatorData())->m_LinkedBlocks; for (auto & itr : Linked) // Check linked powered list { if ( @@ -1984,8 +2052,13 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( itr.a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) ) { - // Check for duplicates, update power level, don't add a new listing - itr.a_PowerLevel = a_PowerLevel; + if (itr.a_PowerLevel != a_PowerLevel) + { + // Update power level, don't add a new listing + Neighbour->SetIsRedstoneDirty(true); + m_Chunk->SetIsRedstoneDirty(true); + itr.a_PowerLevel = a_PowerLevel; + } return; } } @@ -2004,130 +2077,110 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( -void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool WasLastStatePowered) +void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, bool a_WasLastStatePowered) { - for (auto itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr) - { - if (!itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - continue; - } - - if (itr->WasLastStatePowered != WasLastStatePowered) - { - // If power states different, update listing - itr->WasLastStatePowered = WasLastStatePowered; - return; - } - else - { - // If states the same, just ignore - return; - } - } - - // We have arrive here; no block must be in list - add one - sSimulatedPlayerToggleableList RC; - RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); - RC.WasLastStatePowered = WasLastStatePowered; - m_SimulatedPlayerToggleableBlocks->emplace_back(RC); + m_SimulatedPlayerToggleableBlocks->operator[](Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) = a_WasLastStatePowered; } -bool cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn) +void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool a_ShouldPowerOn) { - for (auto itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr) - { - if (itr->a_RelBlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) - { - if (ShouldPowerOn == itr->ShouldPowerOn) // We are queued already for the same thing, don't replace entry - { - return false; - } - - // Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit - itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description - itr->a_ElapsedTicks = 0; - itr->ShouldPowerOn = ShouldPowerOn; - return false; - } - } - - // Self not in list, add self to list sRepeatersDelayList RC; - RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ); // Gets the top two bits (delay time), shifts them into the lower two bits, and adds one (meta 0 = 1 tick; 1 = 2 etc.) // Multiply by 2 because in MCS, 1 redstone tick = 1 world tick, but in Vanilla, 1 redstone tick = 2 world ticks, and we need to maintain compatibility RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; - RC.a_ElapsedTicks = 0; - RC.ShouldPowerOn = ShouldPowerOn; - m_RepeatersDelayList->emplace_back(RC); - return true; + RC.ShouldPowerOn = a_ShouldPowerOn; + + auto Result = m_RepeatersDelayList->emplace(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ), RC); + if (!Result.second) + { + // Key exists + if (a_ShouldPowerOn == Result.first->second.ShouldPowerOn) + { + // We are queued already for the same thing, don't replace entry + return; + } + + Result.first->second.a_DelayTicks = RC.a_DelayTicks; + Result.first->second.a_ElapsedTicks = 0; + Result.first->second.ShouldPowerOn = a_ShouldPowerOn; + } } -void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk, bool a_IsFirstCall) +void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk) { - if (!a_IsFirstCall) // The neighbouring chunks passed when this parameter is false may be invalid + if ((a_Chunk == nullptr) || !a_Chunk->IsValid()) + { + return; + } + std::vector<std::pair<Vector3i, cChunk *>> BlocksPotentiallyUnpowered = { std::make_pair(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ), a_Chunk) }; + + auto UnpoweringFunction = [&BlocksPotentiallyUnpowered](cChunk * a_LambdaChunk, const Vector3i & a_RelSource) { - if ((a_Chunk == nullptr) || !a_Chunk->IsValid()) + BLOCKTYPE RepeaterType; + if (a_LambdaChunk->UnboundedRelGetBlockType(a_RelSource.x, a_RelSource.y, a_RelSource.z, RepeaterType) && (RepeaterType == E_BLOCK_REDSTONE_REPEATER_ON)) { return; } - } - std::vector<Vector3i> BlocksPotentiallyUnpowered; + auto Data = static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_LambdaChunk->GetRedstoneSimulatorData()); + Data->m_PoweredBlocks.erase(std::remove_if(Data->m_PoweredBlocks.begin(), Data->m_PoweredBlocks.end(), [&BlocksPotentiallyUnpowered, a_LambdaChunk, a_RelSource](const sPoweredBlocks & itr) + { + if (itr.m_SourcePos != a_RelSource) + { + return false; + } - auto Data = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); - Data->m_PoweredBlocks.erase(std::remove_if(Data->m_PoweredBlocks.begin(), Data->m_PoweredBlocks.end(), [&BlocksPotentiallyUnpowered, a_Chunk, a_RelSourceX, a_RelSourceY, a_RelSourceZ](const sPoweredBlocks & itr) - { - if (itr.a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ))) + BlocksPotentiallyUnpowered.emplace_back(std::make_pair(itr.m_BlockPos, a_LambdaChunk)); + a_LambdaChunk->SetIsRedstoneDirty(true); + return true; + } + ), Data->m_PoweredBlocks.end()); + + Data->m_LinkedBlocks.erase(std::remove_if(Data->m_LinkedBlocks.begin(), Data->m_LinkedBlocks.end(), [&BlocksPotentiallyUnpowered, a_LambdaChunk, a_RelSource](const sLinkedPoweredBlocks & itr) { - BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos); - a_Chunk->SetIsRedstoneDirty(true); + if (itr.a_SourcePos != a_RelSource) + { + return false; + } + + BlocksPotentiallyUnpowered.emplace_back(std::make_pair(itr.a_BlockPos, a_LambdaChunk)); + a_LambdaChunk->SetIsRedstoneDirty(true); return true; } - return false; - } - ), Data->m_PoweredBlocks.end()); + ), Data->m_LinkedBlocks.end()); - Data->m_LinkedBlocks.erase(std::remove_if(Data->m_LinkedBlocks.begin(), Data->m_LinkedBlocks.end(), [&BlocksPotentiallyUnpowered, a_Chunk, a_RelSourceX, a_RelSourceY, a_RelSourceZ](const sLinkedPoweredBlocks & itr) + for (const auto & BoundaryChunk : GetAdjacentChunks(a_RelSource, a_LambdaChunk)) { - if (itr.a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ))) + auto BoundaryData = static_cast<cIncrementalRedstoneSimulatorChunkData *>(BoundaryChunk->GetRedstoneSimulatorData()); + Vector3i ChunkAdjustedSource = a_RelSource; + ChunkAdjustedSource.x += (a_LambdaChunk->GetPosX() - BoundaryChunk->GetPosX()) * cChunkDef::Width; + ChunkAdjustedSource.z += (a_LambdaChunk->GetPosZ() - BoundaryChunk->GetPosZ()) * cChunkDef::Width; + + if ( + (std::find_if(BoundaryData->m_PoweredBlocks.begin(), BoundaryData->m_PoweredBlocks.end(), [ChunkAdjustedSource](const sPoweredBlocks & itr) { return (itr.m_SourcePos == ChunkAdjustedSource); }) != BoundaryData->m_PoweredBlocks.end()) || + (std::find_if(BoundaryData->m_LinkedBlocks.begin(), BoundaryData->m_LinkedBlocks.end(), [ChunkAdjustedSource](const sLinkedPoweredBlocks & itr) { return (itr.a_SourcePos == ChunkAdjustedSource); }) != BoundaryData->m_LinkedBlocks.end()) + ) { - BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos); - a_Chunk->SetIsRedstoneDirty(true); - return true; + BlocksPotentiallyUnpowered.emplace_back(std::make_pair(ChunkAdjustedSource, BoundaryChunk)); } - return false; } - ), Data->m_LinkedBlocks.end()); - - if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_RelSourceX, a_RelSourceY, a_RelSourceZ)) - { - // +- 2 to accomodate linked powered blocks - SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX - 2, a_RelSourceZ), false); - SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX + 2, a_RelSourceZ), false); - SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX, a_RelSourceZ - 2), false); - SetSourceUnpowered(a_RelSourceX, a_RelSourceY, a_RelSourceZ, a_Chunk->GetRelNeighborChunk(a_RelSourceX, a_RelSourceZ + 2), false); - } + }; - for (const auto & itr : BlocksPotentiallyUnpowered) + while (!BlocksPotentiallyUnpowered.empty()) { - auto Neighbour = a_Chunk->GetRelNeighborChunk(itr.x, itr.z); - if (!AreCoordsPowered(itr.x, itr.y, itr.z) && (Neighbour->GetBlock(itr) != E_BLOCK_REDSTONE_REPEATER_ON)) - { - // Repeaters time themselves with regards to unpowering; ensure we don't do it for them - SetSourceUnpowered(itr.x, itr.y, itr.z, Neighbour); - } + auto End = BlocksPotentiallyUnpowered.back(); + BlocksPotentiallyUnpowered.pop_back(); + UnpoweringFunction(End.second, End.first); } } @@ -2135,45 +2188,48 @@ void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_RelSourceX, int a_R -void cIncrementalRedstoneSimulator::SetInvalidMiddleBlock(int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, cChunk * a_Chunk, bool a_IsFirstCall) +void cIncrementalRedstoneSimulator::SetInvalidMiddleBlock(int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, cChunk * a_Chunk) { - if (!a_IsFirstCall) // The neighbouring chunks passed when this parameter is false may be invalid + std::vector<std::pair<Vector3i, cChunk *>> BlocksPotentiallyUnpowered; + + BLOCKTYPE RepeaterType; + if (a_Chunk->UnboundedRelGetBlockType(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, RepeaterType) && (RepeaterType == E_BLOCK_REDSTONE_REPEATER_ON)) { - if ((a_Chunk == nullptr) || !a_Chunk->IsValid()) - { - return; - } + return; } - std::vector<Vector3i> BlocksPotentiallyUnpowered; - auto Data = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); - - Data->m_LinkedBlocks.erase(std::remove_if(Data->m_LinkedBlocks.begin(), Data->m_LinkedBlocks.end(), [&BlocksPotentiallyUnpowered, a_Chunk, a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ](const sLinkedPoweredBlocks & itr) - { - if (itr.a_MiddlePos.Equals(Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ))) + auto MiddleBlockUnpoweringFunction = [&BlocksPotentiallyUnpowered](cChunk * a_LambdaChunk, const Vector3i & a_RelMiddle) + { + auto Data = static_cast<cIncrementalRedstoneSimulatorChunkData *>(a_LambdaChunk->GetRedstoneSimulatorData()); + Data->m_LinkedBlocks.erase(std::remove_if(Data->m_LinkedBlocks.begin(), Data->m_LinkedBlocks.end(), [&BlocksPotentiallyUnpowered, a_LambdaChunk, a_RelMiddle](const sLinkedPoweredBlocks & itr) { - BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos); - a_Chunk->SetIsRedstoneDirty(true); + if (itr.a_MiddlePos != a_RelMiddle) + { + return false; + } + + BlocksPotentiallyUnpowered.emplace_back(std::make_pair(itr.a_BlockPos, a_LambdaChunk)); + a_LambdaChunk->SetIsRedstoneDirty(true); return true; } - return false; - } - ), Data->m_LinkedBlocks.end()); + ), Data->m_LinkedBlocks.end()); + }; - if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ)) + MiddleBlockUnpoweringFunction(a_Chunk, Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ)); + for (const auto & BoundaryChunk : GetAdjacentChunks(Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ), a_Chunk)) { - // +- 2 to accomodate linked powered blocks - SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX - 2, a_RelMiddleZ), false); - SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX + 2, a_RelMiddleZ), false); - SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX, a_RelMiddleZ - 2), false); - SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX, a_RelMiddleZ + 2), false); + Vector3i ChunkAdjustedMiddlePos = Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ); + ChunkAdjustedMiddlePos.x += (a_Chunk->GetPosX() - BoundaryChunk->GetPosX()) * cChunkDef::Width; + ChunkAdjustedMiddlePos.z += (a_Chunk->GetPosZ() - BoundaryChunk->GetPosZ()) * cChunkDef::Width; + + MiddleBlockUnpoweringFunction(a_Chunk, Vector3i(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ)); } for (const auto & itr : BlocksPotentiallyUnpowered) { - if (!AreCoordsPowered(itr.x, itr.y, itr.z)) + if (!AreCoordsPowered(itr.first.x, itr.first.y, itr.first.z)) { - SetSourceUnpowered(itr.x, itr.y, itr.z, a_Chunk->GetRelNeighborChunk(itr.x, itr.z)); + SetSourceUnpowered(itr.first.x, itr.first.y, itr.first.z, itr.second); } } } @@ -2182,76 +2238,48 @@ void cIncrementalRedstoneSimulator::SetInvalidMiddleBlock(int a_RelMiddleX, int -cIncrementalRedstoneSimulator::eRedstoneDirection cIncrementalRedstoneSimulator::GetWireDirection(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +bool cIncrementalRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) { - int Dir = REDSTONE_NONE; + // Extract the ON bit from metadata and return if true if it is set: + return ((a_BlockMeta & 0x8) == 0x8); +} + + + + + +std::vector<cChunk *> cIncrementalRedstoneSimulator::GetAdjacentChunks(const Vector3i & a_RelBlockPosition, cChunk * a_Chunk) +{ + std::vector<cChunk *> AdjacentChunks; + AdjacentChunks.reserve(2); // At most bordering two chunks; reserve that many - BLOCKTYPE NegX = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, NegX)) + auto CheckAndEmplace = [&AdjacentChunks](cChunk * a_LambdaChunk) { - if (IsPotentialSource(NegX)) + if ((a_LambdaChunk != nullptr) && a_LambdaChunk->IsValid()) { - Dir |= (REDSTONE_X_POS); + AdjacentChunks.emplace_back(a_LambdaChunk); } - } + }; - BLOCKTYPE PosX = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, PosX)) + // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks + if (a_RelBlockPosition.x <= 1) { - if (IsPotentialSource(PosX)) - { - Dir |= (REDSTONE_X_NEG); - } + CheckAndEmplace(a_Chunk->GetRelNeighborChunk(a_RelBlockPosition.x - 2, a_RelBlockPosition.z)); } - - BLOCKTYPE NegZ = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, NegZ)) + if (a_RelBlockPosition.x >= 14) { - if (IsPotentialSource(NegZ)) - { - if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_POS; - } + CheckAndEmplace(a_Chunk->GetRelNeighborChunk(a_RelBlockPosition.x + 2, a_RelBlockPosition.z)); } - - BLOCKTYPE PosZ = 0; - if (m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, PosZ)) + if (a_RelBlockPosition.z <= 1) { - if (IsPotentialSource(PosZ)) - { - if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_NEG; - } + CheckAndEmplace(a_Chunk->GetRelNeighborChunk(a_RelBlockPosition.x, a_RelBlockPosition.z - 2)); + } + if (a_RelBlockPosition.z >= 14) + { + CheckAndEmplace(a_Chunk->GetRelNeighborChunk(a_RelBlockPosition.x, a_RelBlockPosition.z + 2)); } - return (eRedstoneDirection)Dir; -} - - - - -bool cIncrementalRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) -{ - // Extract the ON bit from metadata and return if true if it is set: - return ((a_BlockMeta & 0x8) == 0x8); + return AdjacentChunks; } |