summaryrefslogtreecommitdiffstats
path: root/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h')
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h355
1 files changed, 238 insertions, 117 deletions
diff --git a/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h b/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h
index 5bf4afcd3..2772441bd 100644
--- a/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h
+++ b/src/Simulator/IncrementalRedstoneSimulator/RedstoneWireHandler.h
@@ -2,6 +2,7 @@
#pragma once
#include "RedstoneHandler.h"
+#include "../../Registries/Blocks.h"
@@ -9,198 +10,318 @@
class cRedstoneWireHandler final : public cRedstoneHandler
{
-public:
+ /** A unified representation of wire direction. */
+ enum class TemporaryDirection
+ {
+ Up,
+ Side
+ };
- inline static bool IsDirectlyConnectingMechanism(BLOCKTYPE a_Block, NIBBLETYPE a_BlockMeta, const Vector3i a_Offset)
+ /** Adjusts a given wire block so that the direction represented by Offset has state Direction. */
+ inline static void SetDirectionState(const Vector3i Offset, short & Block, TemporaryDirection Direction)
{
- switch (a_Block)
+ Block = DoWithDirectionState(Offset, Block, [Direction](auto, auto & Front, auto)
{
- case E_BLOCK_REDSTONE_REPEATER_ON:
- case E_BLOCK_REDSTONE_REPEATER_OFF:
+ using FrontState = std::remove_reference_t<decltype(Front)>;
+ switch (Direction)
{
- a_BlockMeta &= E_META_REDSTONE_REPEATER_FACING_MASK;
- if ((a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XP) || (a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XM))
+ case TemporaryDirection::Up:
{
- // Wire connects to repeater if repeater is aligned along X
- // and wire is in front or behind it (#4639)
- return a_Offset.x != 0;
+ Front = FrontState::Up;
+ return;
+ }
+ case TemporaryDirection::Side:
+ {
+ Front = FrontState::Side;
+ return;
}
-
- return a_Offset.z != 0;
}
- case E_BLOCK_ACTIVE_COMPARATOR:
- case E_BLOCK_INACTIVE_COMPARATOR:
- case E_BLOCK_REDSTONE_TORCH_OFF:
- case E_BLOCK_REDSTONE_TORCH_ON:
- case E_BLOCK_REDSTONE_WIRE: return true;
- default: return false;
- }
+ });
}
+ /** Invokes Callback with the wire's left, front, and right direction state corresponding to Offset.
+ Returns a new block constructed from the directions that the callback may have modified. */
template <class OffsetCallback>
- static bool ForTerracingConnectionOffsets(cChunk & a_Chunk, const Vector3i a_Position, OffsetCallback Callback)
+ inline static short DoWithDirectionState(const Vector3i Offset, short Block, OffsetCallback Callback)
+ {
+ auto North = Block::RedstoneWire::North(Block);
+ auto South = Block::RedstoneWire::South(Block);
+ auto West = Block::RedstoneWire::West(Block);
+ auto East = Block::RedstoneWire::East(Block);
+
+ if (Offset.x == -1)
+ {
+ Callback(South, West, North);
+ }
+ else if (Offset.x == 1)
+ {
+ Callback(North, East, South);
+ }
+
+ if (Offset.z == -1)
+ {
+ Callback(West, North, East);
+ }
+ else if (Offset.z == 1)
+ {
+ Callback(East, South, West);
+ }
+
+ return Block::RedstoneWire::RedstoneWire(East, North, 0, South, West);
+ }
+
+public:
+
+ /** Temporary. Discovers a wire's connection state, including terracing, storing the block inside redstone chunk data.
+ TODO: once the server supports block states this should go in the block handler, with data saved in the world. */
+ void SetWireState(const cChunk & Chunk, const Vector3i Position) const
{
- const auto YPTerraceBlock = a_Chunk.GetBlock(a_Position + OffsetYP);
+ auto Block = Block::RedstoneWire::RedstoneWire();
+ const auto YPTerraceBlock = Chunk.GetBlock(Position + OffsetYP);
const bool IsYPTerracingBlocked = cBlockInfo::IsSolid(YPTerraceBlock) && !cBlockInfo::IsTransparent(YPTerraceBlock);
- for (const auto Adjacent : RelativeLaterals)
+ // Loop through laterals, discovering terracing connections:
+ for (const auto Offset : RelativeLaterals)
{
- // All laterals are counted as terracing, duh
- if (Callback(Adjacent))
+ auto Adjacent = Position + Offset;
+ auto NeighbourChunk = Chunk.GetRelNeighborChunkAdjustCoords(Adjacent);
+
+ if ((NeighbourChunk == nullptr) || !NeighbourChunk->IsValid())
{
- return true;
+ continue;
}
- if (
- BLOCKTYPE YPBlock;
+ BLOCKTYPE LateralBlock;
+ NIBBLETYPE LateralMeta;
+ NeighbourChunk->GetBlockTypeMeta(Adjacent, LateralBlock, LateralMeta);
- // A block above us blocks all YP terracing, so the check is static in the loop
- !IsYPTerracingBlocked &&
- a_Chunk.UnboundedRelGetBlockType(a_Position + Adjacent + OffsetYP, YPBlock) &&
- (YPBlock == E_BLOCK_REDSTONE_WIRE)
- )
+ if (IsDirectlyConnectingMechanism(LateralBlock, LateralMeta, Offset))
{
- if (Callback(Adjacent + OffsetYP))
+ // Any direct connections on a lateral means the wire has side connection in that direction:
+ SetDirectionState(Offset, Block, TemporaryDirection::Side);
+
+ // Temporary: this case will eventually be handled when wires are placed, with the state saved as blocks
+ // When a neighbour wire was loaded into its chunk, its neighbour chunks may not have loaded yet
+ // This function is called during chunk load (through AddBlock). Attempt to tell it its new state:
+ if ((NeighbourChunk != &Chunk) && (LateralBlock == E_BLOCK_REDSTONE_WIRE))
{
- return true;
+ auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent)->second;
+ SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Side);
}
+
+ continue;
}
if (
- BLOCKTYPE YMTerraceBlock, YMDiagonalBlock;
-
- // IsYMTerracingBlocked (i.e. check block above lower terracing position, a.k.a. just the plain adjacent)
- a_Chunk.UnboundedRelGetBlockType(a_Position + Adjacent, YMTerraceBlock) &&
- (!cBlockInfo::IsSolid(YMTerraceBlock) || cBlockInfo::IsTransparent(YMTerraceBlock)) &&
-
- a_Chunk.UnboundedRelGetBlockType(a_Position + Adjacent + OffsetYM, YMDiagonalBlock) &&
- (YMDiagonalBlock == E_BLOCK_REDSTONE_WIRE)
+ !IsYPTerracingBlocked && // A block above us blocks all YP terracing, so the check is static in the loop
+ (Adjacent.y < (cChunkDef::Height - 1)) &&
+ (NeighbourChunk->GetBlock(Adjacent + OffsetYP) == E_BLOCK_REDSTONE_WIRE) // Only terrace YP with another wire
)
{
- if (Callback(Adjacent + OffsetYM))
+ SetDirectionState(Offset, Block, TemporaryDirection::Up);
+
+ if (NeighbourChunk != &Chunk)
{
- return true;
+ auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent + OffsetYP)->second;
+ SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Side);
}
+
+ continue;
}
- }
- return false;
- }
+ if (
+ // IsYMTerracingBlocked (i.e. check block above lower terracing position, a.k.a. just the plain adjacent)
+ (!cBlockInfo::IsSolid(LateralBlock) || cBlockInfo::IsTransparent(LateralBlock)) &&
+ (NeighbourChunk->GetBlock(Adjacent + OffsetYM) == E_BLOCK_REDSTONE_WIRE) // Only terrace YM with another wire
+ )
+ {
+ SetDirectionState(Offset, Block, TemporaryDirection::Side);
- virtual unsigned char GetPowerDeliveredToPosition(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, Vector3i a_QueryPosition, BLOCKTYPE a_QueryBlockType) const override
- {
- if (a_QueryPosition == (a_Position + OffsetYP))
- {
- // Wires do not power things above them
- return 0;
+ if (NeighbourChunk != &Chunk)
+ {
+ auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent + OffsetYM)->second;
+ SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Up);
+ }
+ }
}
- if (a_QueryBlockType == E_BLOCK_REDSTONE_WIRE)
+ auto & States = DataForChunk(Chunk).WireStates;
+ const auto FindResult = States.find(Position);
+ if (FindResult != States.end())
{
- // For mechanisms, wire of power one will still power them
- // But for wire-to-wire connections, power level decreases by 1
- return (a_Meta != 0) ? --a_Meta : a_Meta;
- }
+ if (Block != FindResult->second)
+ {
+ FindResult->second = Block;
- // Wires always deliver power to the block underneath, and any directly connecting mechanisms
- if (
- NIBBLETYPE QueryMeta;
+ // TODO: when state is stored as the block, the block handler updating via SetBlock will do this automatically
+ // When a wire changes connection state, it needs to update its neighbours:
+ Chunk.GetWorld()->WakeUpSimulators(cChunkDef::RelativeToAbsolute(Position, Chunk.GetPos()));
+ }
- (a_QueryPosition == (a_Position + OffsetYM)) ||
- (a_Chunk.UnboundedRelGetBlockMeta(a_QueryPosition, QueryMeta) && IsDirectlyConnectingMechanism(a_QueryBlockType, QueryMeta, a_QueryPosition - a_Position))
- )
- {
- return a_Meta;
+ return;
}
- /*
- Okay, we do not directly connect to the wire.
- If there are no DC mechanisms at all, the wire powers all laterals. Great, we fall out the loop.
- If there is one DC mechanism, the wire "goes straight" along the axis of the wire and mechanism.
- The only possible way for us to be powered is for us to be on the opposite end, with the wire pointing towards us.
- If there is more than one DC, no non-DCs are powered.
- */
+ DataForChunk(Chunk).WireStates[Position] = Block;
+ }
- Vector3i PotentialOffset;
- bool FoundOneBorderingMechanism = false;
+private:
- if (
- ForTerracingConnectionOffsets(a_Chunk, a_Position, [&a_Chunk, a_Position, &FoundOneBorderingMechanism, &PotentialOffset](const Vector3i Offset)
+ inline static bool IsDirectlyConnectingMechanism(BLOCKTYPE a_Block, NIBBLETYPE a_BlockMeta, const Vector3i a_Offset)
+ {
+ switch (a_Block)
+ {
+ case E_BLOCK_REDSTONE_REPEATER_ON:
+ case E_BLOCK_REDSTONE_REPEATER_OFF:
{
- BLOCKTYPE Block;
- NIBBLETYPE Meta;
-
- if (
- !a_Chunk.UnboundedRelGetBlock(Offset + a_Position, Block, Meta) ||
- !IsDirectlyConnectingMechanism(Block, Meta, Offset)
- )
+ a_BlockMeta &= E_META_REDSTONE_REPEATER_FACING_MASK;
+ if ((a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XP) || (a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XM))
{
- return false;
+ // Wire connects to repeater if repeater is aligned along X
+ // and wire is in front or behind it (#4639)
+ return a_Offset.x != 0;
}
- if (FoundOneBorderingMechanism)
- {
- // Case 3
- return true;
- }
+ return a_Offset.z != 0;
+ }
+ case E_BLOCK_ACTIVE_COMPARATOR:
+ case E_BLOCK_INACTIVE_COMPARATOR:
+ case E_BLOCK_BLOCK_OF_REDSTONE:
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ case E_BLOCK_REDSTONE_WIRE: return true;
+ default: return false;
+ }
+ }
- // Potential case 2
- FoundOneBorderingMechanism = true;
- PotentialOffset = { -Offset.x, 0, -Offset.z };
+ virtual unsigned char GetPowerDeliveredToPosition(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, Vector3i a_QueryPosition, BLOCKTYPE a_QueryBlockType, bool IsLinked) const override
+ {
+ // Starts off as the wire's meta value, modified appropriately and returned
+ auto Power = a_Chunk.GetMeta(a_Position);
+ const auto QueryOffset = a_QueryPosition - a_Position;
- return false;
- })
+ if (
+ (QueryOffset == OffsetYP) || // Wires do not power things above them
+ (IsLinked && (a_QueryBlockType == E_BLOCK_REDSTONE_WIRE)) // Nor do they link power other wires
)
{
- // Case 3
return 0;
}
- if (FoundOneBorderingMechanism && (a_QueryPosition != (a_Position + PotentialOffset)))
+ if (QueryOffset == OffsetYM)
{
- // Case 2 fail
- return 0;
+ // Wires always deliver power to the block underneath
+ return Power;
}
- // Case 1
- // Case 2 success
+ const auto & Data = DataForChunk(a_Chunk);
+ const auto Block = Data.WireStates.find(a_Position)->second;
+
+ DoWithDirectionState(QueryOffset, Block, [a_QueryBlockType, &Power](const auto Left, const auto Front, const auto Right)
+ {
+ using LeftState = std::remove_reference_t<decltype(Left)>;
+ using FrontState = std::remove_reference_t<decltype(Front)>;
+ using RightState = std::remove_reference_t<decltype(Right)>;
+
+ // Wires always deliver power to any directly connecting mechanisms:
+ if (Front != FrontState::None)
+ {
+ if ((a_QueryBlockType == E_BLOCK_REDSTONE_WIRE) && (Power != 0))
+ {
+ // For mechanisms, wire of power one will still power them
+ // But for wire-to-wire connections, power level decreases by 1:
+ Power--;
+ }
+
+ return;
+ }
+
+ /*
+ Okay, we do not directly connect to the wire.
+ 1. If there are no DC mechanisms at all, the wire powers all laterals. Great, left and right are both None.
+ 2. If there is one DC mechanism, the wire "goes straight" along the axis of the wire and mechanism.
+ The only possible way for us to be powered is for us to be on the opposite end, with the wire pointing towards us.
+ Check that left and right are both None.
+ 3. If there is more than one DC, no non-DCs are powered. Left, right, cannot both be None.
+ */
+ if ((Left == LeftState::None) && (Right == RightState::None))
+ {
+ // Case 1
+ // Case 2
+ return;
+ }
- return a_Meta;
+ // Case 3
+ Power = 0;
+ });
+
+ return Power;
}
virtual void Update(cChunk & a_Chunk, cChunk & CurrentlyTicking, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, PoweringData a_PoweringData) const override
{
// LOGD("Evaluating dusty the wire (%d %d %d) %i", a_Position.x, a_Position.y, a_Position.z, a_PoweringData.PowerLevel);
- if (a_Meta != a_PoweringData.PowerLevel)
+ if (a_Meta == a_PoweringData.PowerLevel)
{
- a_Chunk.SetMeta(a_Position, a_PoweringData.PowerLevel);
+ return;
+ }
- // Notify block below us to update:
- UpdateAdjustedRelative(a_Chunk, CurrentlyTicking, a_Position + OffsetYM);
+ a_Chunk.SetMeta(a_Position, a_PoweringData.PowerLevel);
- // Notify all terracing positions:
- ForTerracingConnectionOffsets(a_Chunk, a_Position, [&a_Chunk, &CurrentlyTicking, a_Position](const Vector3i Offset)
+ // Notify all positions, sans YP, to update:
+ for (const auto Offset : RelativeAdjacents)
+ {
+ if (Offset == OffsetYP)
{
- UpdateAdjustedRelative(a_Chunk, CurrentlyTicking, a_Position + Offset);
- return false;
- });
+ continue;
+ }
+
+ UpdateAdjustedRelative(a_Chunk, CurrentlyTicking, a_Position, Offset);
}
}
- virtual void ForValidSourcePositions(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, SourceCallback Callback) const override
+ virtual void ForValidSourcePositions(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, SourceCallback Callback) const override
{
- UNUSED(a_Chunk);
UNUSED(a_BlockType);
UNUSED(a_Meta);
Callback(a_Position + OffsetYP);
Callback(a_Position + OffsetYM);
- ForTerracingConnectionOffsets(a_Chunk, a_Position, [&Callback, a_Position](const Vector3i Offset)
+ const auto & Data = DataForChunk(a_Chunk);
+ const auto Block = Data.WireStates.find(a_Position)->second;
+
+ // Figure out, based on our pre-computed block, where we connect to:
+ for (const auto Offset : RelativeLaterals)
{
- Callback(a_Position + Offset);
- return false;
- });
+ const auto Relative = a_Position + Offset;
+ Callback(Relative);
+
+ DoWithDirectionState(Offset, Block, [&a_Chunk, &Callback, Relative](auto, const auto Front, auto)
+ {
+ using FrontState = std::remove_reference_t<decltype(Front)>;
+
+ if (Front == FrontState::Up)
+ {
+ Callback(Relative + OffsetYP);
+ }
+ else if (Front == FrontState::Side)
+ {
+ // Alas, no way to distinguish side lateral and side diagonal
+ // Have to do a manual check to only accept power from YM diagonal if there's a wire there
+
+ const auto YMDiagonalPosition = Relative + OffsetYM;
+ if (
+ BLOCKTYPE Block;
+ cChunkDef::IsValidHeight(YMDiagonalPosition.y) &&
+ a_Chunk.UnboundedRelGetBlockType(YMDiagonalPosition, Block) &&
+ (Block == E_BLOCK_REDSTONE_WIRE)
+ )
+ {
+ Callback(YMDiagonalPosition);
+ }
+ }
+ });
+ }
}
};