From bd484750856586bd2354be2aa6852a63fd102c1b Mon Sep 17 00:00:00 2001
From: Tiger Wang <ziwei.tiger@hotmail.co.uk>
Date: Fri, 20 Mar 2015 22:30:20 +0000
Subject: [PATCH] Provides improvements to redstone wire

Intermediary commit that fixes #1763.
---
 .../IncrementalRedstoneSimulator.cpp          | 147 ++++++++----------
 src/Simulator/IncrementalRedstoneSimulator.h  |  19 +--
 2 files changed, 71 insertions(+), 95 deletions(-)

diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp
index c1a66243d..ff813a2c0 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.cpp
+++ b/src/Simulator/IncrementalRedstoneSimulator.cpp
@@ -304,7 +304,7 @@ 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))
+	if (GetCoordinateAdjacentChunk({ a_BlockX, a_BlockY, a_BlockZ }).Equals({ 0, 0, 0 }))
 	{
 		// On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk)
 		AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
@@ -1980,35 +1980,6 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(Vector3i a_RelBlockPosition,
 		}
 	}
 
-	//// 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 == a_RelBlockPosition) &&
-	//		(itr->a_SourcePos == a_RelSourcePosition) &&
-	//		(m_Chunk->GetBlock(a_RelSourcePosition) == E_BLOCK_REDSTONE_WIRE)
-	//		)
-	//	{
-	//		BLOCKTYPE Block;
-	//		NIBBLETYPE Meta;
-	//		Neighbour->GetBlockTypeMeta(a_RelBlockPosition.x, a_RelBlockPosition.y, a_RelBlockPosition.z, Block, Meta);
-
-	//		if (Block == E_BLOCK_REDSTONE_WIRE)
-	//		{
-	//			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;
-	//			}
-	//		}
-	//	}
-	//}
-
 	sPoweredBlocks RC;
 	RC.a_BlockPos = a_RelBlockPosition;
 	RC.a_SourcePos = a_RelSourcePosition;
@@ -2079,20 +2050,12 @@ void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_Re
 			continue;
 		}
 
-		if (itr->WasLastStatePowered != WasLastStatePowered)
-		{
-			// If power states different, update listing
-			itr->WasLastStatePowered = WasLastStatePowered;
-			return;
-		}
-		else
-		{
-			// If states the same, just ignore
-			return;
-		}
+		// Update listing
+		itr->WasLastStatePowered = WasLastStatePowered;
+		return;
 	}
 
-	// We have arrive here; no block must be in list - add one
+	// We have arrived here; no block must be in list - add one
 	sSimulatedPlayerToggleableList RC;
 	RC.a_RelBlockPos = Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ);
 	RC.WasLastStatePowered = WasLastStatePowered;
@@ -2140,60 +2103,72 @@ bool cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, in
 
 
 
-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
+	std::vector<std::pair<Vector3i, cChunk *>> BlocksPotentiallyUnpowered = { std::make_pair(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ), a_Chunk) };
+
+	auto UnpoweringFunction = [&BlocksPotentiallyUnpowered](cChunk * a_Chunk, const Vector3i & a_RelSource)
 	{
-		if ((a_Chunk == nullptr) || !a_Chunk->IsValid())
-		{
-			return;
-		}
-	}
-
-	std::vector<Vector3i> BlocksPotentiallyUnpowered;
-
-	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)))
+		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_RelSource](const sPoweredBlocks & itr)
 			{
-				BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos);
+				if (itr.a_SourcePos != a_RelSource)
+				{
+					return false;
+				}
+
+				auto ChunkBoundaryTestCoords = a_RelSource + GetCoordinateAdjacentChunk(a_RelSource);
+				auto BoundaryChunk = a_Chunk->GetRelNeighborChunk(ChunkBoundaryTestCoords.x, ChunkBoundaryTestCoords.z);
+				if (BoundaryChunk != a_Chunk)
+				{
+					Vector3i ChunkAdjustedSource = a_RelSource;
+					ChunkAdjustedSource.x += (a_Chunk->GetPosX() - BoundaryChunk->GetPosX()) * cChunkDef::Width;
+					ChunkAdjustedSource.z += (a_Chunk->GetPosZ() - BoundaryChunk->GetPosZ()) * cChunkDef::Width;
+
+					auto BoundaryData = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)BoundaryChunk->GetRedstoneSimulatorData();
+					BoundaryData->m_PoweredBlocks.erase(std::remove_if(BoundaryData->m_PoweredBlocks.begin(), BoundaryData->m_PoweredBlocks.end(), [&BlocksPotentiallyUnpowered, BoundaryChunk, ChunkAdjustedSource](const sPoweredBlocks & itr)
+						{
+							if (itr.a_SourcePos != ChunkAdjustedSource)
+							{
+								return false;
+							}
+
+							BlocksPotentiallyUnpowered.emplace_back(std::make_pair(itr.a_BlockPos, BoundaryChunk));
+							BoundaryChunk->SetIsRedstoneDirty(true);
+							return true;
+						}
+					), BoundaryData->m_PoweredBlocks.end());
+				}
+
+				if (a_Chunk->GetBlock(a_RelSource) == E_BLOCK_REDSTONE_REPEATER_ON)
+				{
+					return false;
+				}
+
+				BlocksPotentiallyUnpowered.emplace_back(std::make_pair(itr.a_BlockPos, a_Chunk));
 				a_Chunk->SetIsRedstoneDirty(true);
 				return true;
 			}
-			return false;
-		}
-	), Data->m_PoweredBlocks.end());
+		), Data->m_PoweredBlocks.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)
-		{
-			if (itr.a_SourcePos.Equals(Vector3i(a_RelSourceX, a_RelSourceY, a_RelSourceZ)))
+		/*Data->m_LinkedBlocks.erase(std::remove_if(Data->m_LinkedBlocks.begin(), Data->m_LinkedBlocks.end(), [&BlocksPotentiallyUnpowered, a_Chunk, a_RelSource](const sLinkedPoweredBlocks & itr)
 			{
-				BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos);
-				a_Chunk->SetIsRedstoneDirty(true);
-				return true;
+				if (itr.a_SourcePos == a_RelSource)
+				{
+					BlocksPotentiallyUnpowered.emplace_back(itr.a_BlockPos);
+					a_Chunk->SetIsRedstoneDirty(true);
+					return true;
+				}
+				return false;
 			}
-			return false;
-		}
-	), Data->m_LinkedBlocks.end());
+		), Data->m_LinkedBlocks.end());*/
+	};
 
-	if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_RelSourceX, a_RelSourceY, a_RelSourceZ))
+	while (!BlocksPotentiallyUnpowered.empty())
 	{
-		// +- 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)
-	{
-		auto Neighbour = a_Chunk->GetRelNeighborChunk(itr.x, itr.z);
-		if (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);
 	}
 }
 
@@ -2226,7 +2201,7 @@ void cIncrementalRedstoneSimulator::SetInvalidMiddleBlock(int a_RelMiddleX, int
 		}
 	), Data->m_LinkedBlocks.end());
 
-	if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ))
+	if (a_IsFirstCall && /*AreCoordsOnChunkBoundary(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ)*/1)
 	{
 		// +- 2 to accomodate linked powered blocks
 		SetInvalidMiddleBlock(a_RelMiddleX, a_RelMiddleY, a_RelMiddleZ, a_Chunk->GetRelNeighborChunk(a_RelMiddleX - 2, a_RelMiddleZ), false);
diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h
index 65e3ff4af..903dbe82a 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.h
+++ b/src/Simulator/IncrementalRedstoneSimulator.h
@@ -22,7 +22,8 @@ class cIncrementalRedstoneSimulator :
 public:
 
 	cIncrementalRedstoneSimulator(cWorld & a_World)
-		: cRedstoneSimulator(a_World)
+		: cRedstoneSimulator(a_World),
+		m_Chunk(nullptr)
 	{
 	}
 
@@ -209,7 +210,7 @@ private:
 	/** Removes a block from the Powered and LinkedPowered lists
 	Used for variable sources such as tripwire hooks, daylight sensors, and trapped chests
 	*/
-	void SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk, bool a_IsFirstCall = true);
+	void SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk);
 	void SetInvalidMiddleBlock(int a_RelMiddleX, int a_RelMiddleY, int a_RelMiddleZ, cChunk * a_Chunk, bool a_IsFirstCall = true);
 
 	/** Returns if a coordinate is powered or linked powered */
@@ -384,14 +385,14 @@ private:
 		}
 	}
 
-	inline static bool AreCoordsOnChunkBoundary(int a_BlockX, int a_BlockY, int a_BlockZ)
+	inline static Vector3i GetCoordinateAdjacentChunk(const Vector3i &  a_BlockPos)
 	{
-		return (  // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks
-			((a_BlockX % cChunkDef::Width) <= 1) ||
-			((a_BlockX % cChunkDef::Width) >= 14) ||
-			((a_BlockZ % cChunkDef::Width) <= 1) ||
-			((a_BlockZ % cChunkDef::Width) >= 14)
-			);
+		// Are we on a chunk boundary? +- 2 because of LinkedPowered blocks
+		if ((a_BlockPos.x % cChunkDef::Width) <= 1) { return{ -2, 0, 0 }; }
+		if ((a_BlockPos.x % cChunkDef::Width) >= 14) { return{ 2, 0, 0 }; }
+		if ((a_BlockPos.z % cChunkDef::Width) <= 1) { return{ 0, 0, -2 }; }
+		if ((a_BlockPos.z % cChunkDef::Width) >= 14) { return{ 0, 0, 2 }; }
+		return { 0, 0, 0 };
 	}
 
 	inline static Vector3i AdjustRelativeCoords(const Vector3i & a_RelPosition)