Cuberite
A lightweight, fast and extensible game server for Minecraft
RedstoneWireHandler.h
Go to the documentation of this file.
1 
2 #pragma once
3 
4 #include "RedstoneHandler.h"
6 
7 
8 
9 
10 
12 {
14  enum class TemporaryDirection
15  {
16  Up,
17  Side
18  };
19 
22  template <class OffsetCallback>
23  static BlockState DoWithDirectionState(const Vector3i Offset, BlockState Block, OffsetCallback Callback)
24  {
29 
30  if (Offset.x == -1)
31  {
32  Callback(South, West, North);
33  }
34  else if (Offset.x == 1)
35  {
36  Callback(North, East, South);
37  }
38 
39  if (Offset.z == -1)
40  {
41  Callback(West, North, East);
42  }
43  else if (Offset.z == 1)
44  {
45  Callback(East, South, West);
46  }
47 
49  }
50 
53  {
54  Block = DoWithDirectionState(Offset, Block, [Direction](auto, auto & Front, auto)
55  {
56  using FrontState = std::remove_reference_t<decltype(Front)>;
57  switch (Direction)
58  {
60  {
61  Front = FrontState::Up;
62  return;
63  }
65  {
66  Front = FrontState::Side;
67  return;
68  }
69  }
70  });
71  }
72 
73  static bool IsDirectlyConnectingMechanism(BLOCKTYPE a_Block, NIBBLETYPE a_BlockMeta, const Vector3i a_Offset)
74  {
75  switch (a_Block)
76  {
79  {
81  if ((a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XP) || (a_BlockMeta == E_META_REDSTONE_REPEATER_FACING_XM))
82  {
83  // Wire connects to repeater if repeater is aligned along X
84  // and wire is in front or behind it (#4639)
85  return a_Offset.x != 0;
86  }
87 
88  return a_Offset.z != 0;
89  }
93  case E_BLOCK_LEVER:
98  case E_BLOCK_WOODEN_BUTTON: return true;
99  default: return false;
100  }
101  }
102 
103  static bool IsYPTerracingBlocked(const cChunk & a_Chunk, const Vector3i a_Position)
104  {
105  const auto Position = a_Position + OffsetYP;
106 
107  if (!cChunkDef::IsValidHeight(Position))
108  {
109  // Certainly cannot terrace at the top of the world:
110  return true;
111  }
112 
113  const auto YPTerraceBlock = a_Chunk.GetBlock(Position);
114  return cBlockInfo::IsSolid(YPTerraceBlock) && !cBlockInfo::IsTransparent(YPTerraceBlock);
115  }
116 
119  static void SetWireState(const cChunk & a_Chunk, const Vector3i a_Position)
120  {
122  const bool IsYPTerracingBlocked = RedstoneWireHandler::IsYPTerracingBlocked(a_Chunk, a_Position);
123 
124  // Loop through laterals, discovering terracing connections:
125  for (const auto & Offset : RelativeLaterals)
126  {
127  auto Adjacent = a_Position + Offset;
128  auto NeighbourChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(Adjacent);
129 
130  if ((NeighbourChunk == nullptr) || !NeighbourChunk->IsValid())
131  {
132  continue;
133  }
134 
135  BLOCKTYPE LateralBlock;
136  NIBBLETYPE LateralMeta;
137  NeighbourChunk->GetBlockTypeMeta(Adjacent, LateralBlock, LateralMeta);
138 
139  if (IsDirectlyConnectingMechanism(LateralBlock, LateralMeta, Offset))
140  {
141  // Any direct connections on a lateral means the wire has side connection in that direction:
143 
144  // Temporary: this case will eventually be handled when wires are placed, with the state saved as blocks
145  // When a neighbour wire was loaded into its chunk, its neighbour chunks may not have loaded yet
146  // This function is called during chunk load (through AddBlock). Attempt to tell it its new state:
147  if ((NeighbourChunk != &a_Chunk) && (LateralBlock == E_BLOCK_REDSTONE_WIRE))
148  {
149  auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent)->second;
150  SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Side);
151  }
152 
153  continue;
154  }
155 
156  if (
157  !IsYPTerracingBlocked && // A block above us blocks all YP terracing, so the check is static in the loop
158  (Adjacent.y < (cChunkDef::Height - 1)) &&
159  (NeighbourChunk->GetBlock(Adjacent + OffsetYP) == E_BLOCK_REDSTONE_WIRE) // Only terrace YP with another wire
160  )
161  {
163 
164  if (NeighbourChunk != &a_Chunk)
165  {
166  auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent + OffsetYP)->second;
167  SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Side);
168  }
169 
170  continue;
171  }
172 
173  if (
174  // IsYMTerracingBlocked (i.e. check block above lower terracing position, a.k.a. just the plain adjacent)
175  (!cBlockInfo::IsSolid(LateralBlock) || cBlockInfo::IsTransparent(LateralBlock)) &&
176  (Adjacent.y > 0) &&
177  (NeighbourChunk->GetBlock(Adjacent + OffsetYM) == E_BLOCK_REDSTONE_WIRE) // Only terrace YM with another wire
178  )
179  {
181 
182  if (NeighbourChunk != &a_Chunk)
183  {
184  auto & NeighbourBlock = DataForChunk(*NeighbourChunk).WireStates.find(Adjacent + OffsetYM)->second;
185  SetDirectionState(-Offset, NeighbourBlock, TemporaryDirection::Up);
186  }
187  }
188  }
189 
190  auto & States = DataForChunk(a_Chunk).WireStates;
191  const auto FindResult = States.find(a_Position);
192  if (FindResult != States.end())
193  {
194  if (Block != FindResult->second)
195  {
196  FindResult->second = Block;
197 
198  // TODO: when state is stored as the block, the block handler updating via SetBlock will do this automatically
199  // When a wire changes connection state, it needs to update its neighbours:
200  a_Chunk.GetWorld()->WakeUpSimulators(cChunkDef::RelativeToAbsolute(a_Position, a_Chunk.GetPos()));
201  }
202 
203  return;
204  }
205 
206  DataForChunk(a_Chunk).WireStates.emplace(a_Position, Block);
207  }
208 
209  static PowerLevel GetPowerDeliveredToPosition(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, Vector3i a_QueryPosition, BLOCKTYPE a_QueryBlockType, bool IsLinked)
210  {
211  // Starts off as the wire's meta value, modified appropriately and returned
212  auto Power = a_Chunk.GetMeta(a_Position);
213  const auto QueryOffset = a_QueryPosition - a_Position;
214 
215  if (
216  (QueryOffset == OffsetYP) || // Wires do not power things above them
217  (IsLinked && (a_QueryBlockType == E_BLOCK_REDSTONE_WIRE)) // Nor do they link power other wires
218  )
219  {
220  return 0;
221  }
222 
223  if (QueryOffset == OffsetYM)
224  {
225  // Wires always deliver power to the block underneath
226  return Power;
227  }
228 
229  const auto & Data = DataForChunk(a_Chunk);
230  const auto Block = Data.WireStates.find(a_Position)->second;
231 
232  DoWithDirectionState(QueryOffset, Block, [a_QueryBlockType, &Power](const auto Left, const auto Front, const auto Right)
233  {
234  using LeftState = std::remove_reference_t<decltype(Left)>;
235  using FrontState = std::remove_reference_t<decltype(Front)>;
236  using RightState = std::remove_reference_t<decltype(Right)>;
237 
238  // Wires always deliver power to any directly connecting mechanisms:
239  if (Front != FrontState::None)
240  {
241  if ((a_QueryBlockType == E_BLOCK_REDSTONE_WIRE) && (Power != 0))
242  {
243  // For mechanisms, wire of power one will still power them
244  // But for wire-to-wire connections, power level decreases by 1:
245  Power--;
246  }
247 
248  return;
249  }
250 
251  /*
252  Okay, we do not directly connect to the wire.
253  1. If there are no DC mechanisms at all, the wire powers all laterals. Great, left and right are both None.
254  2. If there is one DC mechanism, the wire "goes straight" along the axis of the wire and mechanism.
255  The only possible way for us to be powered is for us to be on the opposite end, with the wire pointing towards us.
256  Check that left and right are both None.
257  3. If there is more than one DC, no non-DCs are powered. Left, right, cannot both be None.
258  */
259  if ((Left == LeftState::None) && (Right == RightState::None))
260  {
261  // Case 1
262  // Case 2
263  return;
264  }
265 
266  // Case 3
267  Power = 0;
268  });
269 
270  return Power;
271  }
272 
273  static void Update(cChunk & a_Chunk, cChunk & CurrentlyTicking, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, const PowerLevel Power)
274  {
275  // LOGD("Evaluating dusty the wire (%d %d %d) %i", a_Position.x, a_Position.y, a_Position.z, Power);
276 
277  if (a_Meta == Power)
278  {
279  return;
280  }
281 
282  a_Chunk.SetMeta(a_Position, Power);
283 
284  // Notify all positions, sans YP, to update:
285  UpdateAdjustedRelative(a_Chunk, CurrentlyTicking, a_Position, OffsetYM);
286  UpdateAdjustedRelatives(a_Chunk, CurrentlyTicking, a_Position, RelativeLaterals);
287  }
288 
289  static void ForValidSourcePositions(const cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, ForEachSourceCallback & Callback)
290  {
291  UNUSED(a_BlockType);
292  UNUSED(a_Meta);
293 
294  Callback(a_Position + OffsetYP);
295  Callback(a_Position + OffsetYM);
296 
297  const auto & Data = DataForChunk(a_Chunk);
298  const auto Block = Data.WireStates.find(a_Position)->second;
299 
300  // Figure out, based on our pre-computed block, where we connect to:
301  for (const auto & Offset : RelativeLaterals)
302  {
303  const auto Relative = a_Position + Offset;
304  Callback(Relative);
305 
306  DoWithDirectionState(Offset, Block, [&a_Chunk, &Callback, Relative](auto, const auto Front, auto)
307  {
308  using FrontState = std::remove_reference_t<decltype(Front)>;
309 
310  if (Front == FrontState::Up)
311  {
312  Callback(Relative + OffsetYP);
313  }
314  else if (Front == FrontState::Side)
315  {
316  // Alas, no way to distinguish side lateral and side diagonal
317  // Have to do a manual check to only accept power from YM diagonal if there's a wire there
318 
319  const auto YMDiagonalPosition = Relative + OffsetYM;
320  if (
321  BLOCKTYPE QueryBlock;
322  cChunkDef::IsValidHeight(YMDiagonalPosition) &&
323  a_Chunk.UnboundedRelGetBlockType(YMDiagonalPosition, QueryBlock) &&
324  (QueryBlock == E_BLOCK_REDSTONE_WIRE)
325  )
326  {
327  Callback(YMDiagonalPosition);
328  }
329  }
330  });
331  }
332  }
333 };
@ E_META_REDSTONE_REPEATER_FACING_XP
Definition: BlockType.h:799
@ E_META_REDSTONE_REPEATER_FACING_MASK
Definition: BlockType.h:802
@ E_META_REDSTONE_REPEATER_FACING_XM
Definition: BlockType.h:801
@ E_BLOCK_WOODEN_BUTTON
Definition: BlockType.h:158
@ E_BLOCK_REDSTONE_TORCH_ON
Definition: BlockType.h:90
@ E_BLOCK_REDSTONE_REPEATER_ON
Definition: BlockType.h:109
@ E_BLOCK_BLOCK_OF_REDSTONE
Definition: BlockType.h:168
@ E_BLOCK_LEVER
Definition: BlockType.h:83
@ E_BLOCK_INACTIVE_COMPARATOR
Definition: BlockType.h:165
@ E_BLOCK_ACTIVE_COMPARATOR
Definition: BlockType.h:166
@ E_BLOCK_REDSTONE_WIRE
Definition: BlockType.h:65
@ E_BLOCK_REDSTONE_TORCH_OFF
Definition: BlockType.h:89
@ E_BLOCK_REDSTONE_REPEATER_OFF
Definition: BlockType.h:108
@ E_BLOCK_STONE_BUTTON
Definition: BlockType.h:91
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:44
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
#define UNUSED
Definition: Globals.h:72
Direction
constexpr Vector3i OffsetYP
auto & DataForChunk(const cChunk &a_Chunk)
constexpr Vector3i OffsetYM
constexpr std::array< Vector3i, 4 > RelativeLaterals
void UpdateAdjustedRelatives(const cChunk &a_Chunk, const cChunk &a_TickingChunk, const Vector3i a_Position, const ArrayType &a_Relative)
void UpdateAdjustedRelative(const cChunk &a_Chunk, const cChunk &a_TickingChunk, const Vector3i a_Position, const Vector3i a_Offset)
unsigned char PowerLevel
bool Up(const BlockState Block)
unsigned char Power(const BlockState Block)
BlockState RedstoneWire()
static bool IsYPTerracingBlocked(const cChunk &a_Chunk, const Vector3i a_Position)
TemporaryDirection
A unified representation of wire direction.
static void Update(cChunk &a_Chunk, cChunk &CurrentlyTicking, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, const PowerLevel Power)
static void SetDirectionState(const Vector3i Offset, BlockState &Block, TemporaryDirection Direction)
Adjusts a given wire block so that the direction represented by Offset has state Direction.
static void ForValidSourcePositions(const cChunk &a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta, ForEachSourceCallback &Callback)
static PowerLevel GetPowerDeliveredToPosition(const cChunk &a_Chunk, Vector3i a_Position, BLOCKTYPE a_BlockType, Vector3i a_QueryPosition, BLOCKTYPE a_QueryBlockType, bool IsLinked)
static void SetWireState(const cChunk &a_Chunk, const Vector3i a_Position)
Temporary.
static bool IsDirectlyConnectingMechanism(BLOCKTYPE a_Block, NIBBLETYPE a_BlockMeta, const Vector3i a_Offset)
static BlockState DoWithDirectionState(const Vector3i Offset, BlockState Block, OffsetCallback Callback)
Invokes Callback with the wire's left, front, and right direction state corresponding to Offset.
Represents the state of a single block (previously known as "block meta").
Definition: BlockState.h:20
static bool IsSolid(BLOCKTYPE Block)
Is this block solid (player cannot walk through)?
Definition: BlockInfo.cpp:892
static bool IsTransparent(BLOCKTYPE Block)
Is a block transparent? (https://minecraft.wiki/w/Opacity)
Definition: BlockInfo.cpp:961
Definition: Chunk.h:36
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:279
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:146
cChunk * GetRelNeighborChunkAdjustCoords(Vector3i &a_RelPos) const
Returns the chunk into which the relatively-specified block belongs.
Definition: Chunk.cpp:1885
cChunkCoords GetPos() const
Definition: Chunk.h:133
void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta)
Definition: Chunk.h:286
cWorld * GetWorld(void) const
Definition: Chunk.h:135
bool UnboundedRelGetBlockType(Vector3i a_RelCoords, BLOCKTYPE &a_BlockType) const
Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap...
Definition: Chunk.cpp:1029
void GetBlockTypeMeta(Vector3i a_RelPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) const
Definition: Chunk.cpp:1757
static bool IsValidHeight(Vector3i a_BlockPosition)
Validates a height-coordinate.
Definition: ChunkDef.h:185
static Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition, cChunkCoords a_ChunkCoords)
Converts relative block coordinates into absolute coordinates with a known chunk location.
Definition: ChunkDef.h:174
static const int Height
Definition: ChunkDef.h:125
T x
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
virtual void WakeUpSimulators(Vector3i a_Block) override
Wakes up the simulators for the specified block.
Definition: World.cpp:1357