Cuberite
A lightweight, fast and extensible game server for Minecraft
BlockFire.h
Go to the documentation of this file.
1 
2 #pragma once
3 
4 #include "BlockHandler.h"
5 
6 
7 
8 
9 
10 class cBlockFireHandler final :
11  public cBlockHandler
12 {
13 public:
14 
16 
17 private:
18 
19  struct Scratch
20  {
22  int XZP = 0, XZM = 0;
24  };
25 
26 
27 
28 
29 
30  virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) const override
31  {
32  /*
33  PORTAL FINDING ALGORITH
34  =======================
35  - Get clicked base block
36  - Trace upwards to find first obsidian block; aborts if anything other than obsidian or air is encountered.
37  Uses this value as a reference (the 'ceiling')
38  - For both directions (if one fails, try the other), BASE (clicked) block:
39  - Go in one direction, only stop if a non obsidian block is encountered (abort) OR a portal border is encountered (FindObsidianCeiling returns -1)
40  - If a border was encountered, go the other direction and repeat above
41  - Write borders to XZP and XZM, write direction portal faces to Dir
42  - Loop through boundary variables, and fill with portal blocks based on Dir with meta from Dir
43  */
44 
45  if (a_WorldInterface.GetDimension() == dimEnd)
46  {
47  // Can only create portals in the Nether and Overworld (GH #5009):
48  return;
49  }
50 
52 
53  // a_BlockY - 1: Because we want the block below the fire
54  FindAndSetPortalFrame(a_BlockPos.x, a_BlockPos.y - 1, a_BlockPos.z, a_ChunkInterface, a_WorldInterface, Scratch);
55  }
56 
57 
58 
59 
60 
61  virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
62  {
63  // No pickups from this block
64  return {};
65  }
66 
67 
70  static int FindObsidianCeiling(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, int MaxY = 0)
71  {
72  if (a_ChunkInterface.GetBlock({X, Y, Z}) != E_BLOCK_OBSIDIAN)
73  {
74  return 0;
75  }
76 
77  for (int newY = Y + 1; newY < cChunkDef::Height; newY++)
78  {
79  BLOCKTYPE Block = a_ChunkInterface.GetBlock({X, newY, Z});
80  if ((Block == E_BLOCK_AIR) || (Block == E_BLOCK_FIRE))
81  {
82  continue;
83  }
84  else if (Block == E_BLOCK_OBSIDIAN)
85  {
86  // We found an obsidian ceiling
87  // Make sure MaxY has a value and newY ('ceiling' location) is at one above the base block
88  // This is because the frame is a solid obsidian pillar
89  if ((MaxY != 0) && (newY == Y + 1))
90  {
91  return EvaluatePortalBorder(X, newY, Z, MaxY, a_ChunkInterface) ? -1 /* -1 = found a frame */ : 0;
92  }
93  else
94  {
95  // Return ceiling Y, whoever called this function will decide if it's part of a portal or not
96  return newY;
97  }
98  }
99  }
100 
101  return 0;
102  }
103 
105  static bool EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cChunkInterface & a_ChunkInterface)
106  {
107  for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners
108  {
109  if (a_ChunkInterface.GetBlock({X, checkBorder, Z}) != E_BLOCK_OBSIDIAN)
110  {
111  // Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal
112  return false;
113  }
114  }
115  // Everything was obsidian, found a border!
116  return true;
117  }
118 
119 
120 
121 
122 
124  static void FindAndSetPortalFrame(int X, int Y, int Z, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Scratch & a_Scratch)
125  {
126  int MaxY = FindObsidianCeiling(X, Y, Z, a_ChunkInterface); // Get topmost obsidian block as reference for all other checks
127  int X1 = X + 1, Z1 = Z + 1, X2 = X - 1, Z2 = Z - 1; // Duplicate XZ values, add / subtract one as we've checked the original already the line above
128 
129  if (MaxY == 0) // Oh noes! Not a portal coordinate :(
130  {
131  return;
132  }
133 
134  if (!FindPortalSliceX(X1, X2, Y, Z, MaxY, a_ChunkInterface, a_Scratch))
135  {
136  if (!FindPortalSliceZ(X, Y, Z1, Z2, MaxY, a_ChunkInterface, a_Scratch))
137  {
138  return; // No eligible portal construct, abort abort abort!!
139  }
140  }
141 
142  int PortalHeight = MaxY - Y - 1;
143  int PortalWidth = a_Scratch.XZP - a_Scratch.XZM + 1;
144  if ((PortalHeight < a_WorldInterface.GetMinNetherPortalHeight()) || (PortalHeight > a_WorldInterface.GetMaxNetherPortalHeight()))
145  {
146  // The portal isn't high enough, or is too high
147  return;
148  }
149 
150  if ((PortalWidth < a_WorldInterface.GetMinNetherPortalWidth()) || (PortalWidth > a_WorldInterface.GetMaxNetherPortalWidth()))
151  {
152  // The portal isn't wide enough, or is too wide
153  return;
154  }
155 
156  for (int Height = Y + 1; Height <= MaxY - 1; Height++) // Loop through boundary to set portal blocks
157  {
158  for (int Width = a_Scratch.XZM; Width <= a_Scratch.XZP; Width++)
159  {
160  if (a_Scratch.Dir == 1)
161  {
162  a_ChunkInterface.SetBlock(Width, Height, Z, E_BLOCK_NETHER_PORTAL, a_Scratch.Dir);
163  }
164  else
165  {
166  a_ChunkInterface.SetBlock(X, Height, Width, E_BLOCK_NETHER_PORTAL, a_Scratch.Dir);
167  }
168  }
169  }
170  }
171 
174  static bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cChunkInterface & a_ChunkInterface, Scratch & a_Scratch)
175  {
176  a_Scratch.Dir = 1; // Set assumed direction (will change if portal turns out to be facing the other direction)
177  bool FoundFrameXP = false, FoundFrameXM = false;
178  for (; ((a_ChunkInterface.GetBlock({X1, Y, Z}) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock({X1, Y + 1, Z}) == E_BLOCK_OBSIDIAN)); X1++) // Check XP for obsidian blocks, exempting corners
179  {
180  int Value = FindObsidianCeiling(X1, Y, Z, a_ChunkInterface, MaxY);
181  int ValueTwo = FindObsidianCeiling(X1, Y + 1, Z, a_ChunkInterface, MaxY); // For corners without obsidian
182  if ((Value == -1) || (ValueTwo == -1)) // FindObsidianCeiling returns -1 upon frame-find
183  {
184  FoundFrameXP = true; // Found a frame border in this direction, proceed in other direction (don't go further)
185  break;
186  }
187  else if ((Value != MaxY) && (ValueTwo != MaxY)) // Make sure that there is a valid portal 'slice'
188  {
189  return false; // Not valid slice, no portal can be formed
190  }
191  }
192  a_Scratch.XZP = X1 - 1; // Set boundary of frame interior
193  for (; ((a_ChunkInterface.GetBlock({X2, Y, Z}) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock({X2, Y + 1, Z}) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM)
194  {
195  int Value = FindObsidianCeiling(X2, Y, Z, a_ChunkInterface, MaxY);
196  int ValueTwo = FindObsidianCeiling(X2, Y + 1, Z, a_ChunkInterface, MaxY);
197  if ((Value == -1) || (ValueTwo == -1))
198  {
199  FoundFrameXM = true;
200  break;
201  }
202  else if ((Value != MaxY) && (ValueTwo != MaxY))
203  {
204  return false;
205  }
206  }
207  a_Scratch.XZM = X2 + 1; // Set boundary, see previous
208 
209  return (FoundFrameXP && FoundFrameXM);
210  }
211 
213  static bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cChunkInterface & a_ChunkInterface, Scratch & a_Scratch)
214  {
215  a_Scratch.Dir = 2;
216  bool FoundFrameZP = false, FoundFrameZM = false;
217  for (; ((a_ChunkInterface.GetBlock({X, Y, Z1}) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock({X, Y + 1, Z1}) == E_BLOCK_OBSIDIAN)); Z1++)
218  {
219  int Value = FindObsidianCeiling(X, Y, Z1, a_ChunkInterface, MaxY);
220  int ValueTwo = FindObsidianCeiling(X, Y + 1, Z1, a_ChunkInterface, MaxY);
221  if ((Value == -1) || (ValueTwo == -1))
222  {
223  FoundFrameZP = true;
224  break;
225  }
226  else if ((Value != MaxY) && (ValueTwo != MaxY))
227  {
228  return false;
229  }
230  }
231  a_Scratch.XZP = Z1 - 1;
232  for (; ((a_ChunkInterface.GetBlock({X, Y, Z2}) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock({X, Y + 1, Z2}) == E_BLOCK_OBSIDIAN)); Z2--)
233  {
234  int Value = FindObsidianCeiling(X, Y, Z2, a_ChunkInterface, MaxY);
235  int ValueTwo = FindObsidianCeiling(X, Y + 1, Z2, a_ChunkInterface, MaxY);
236  if ((Value == -1) || (ValueTwo == -1))
237  {
238  FoundFrameZM = true;
239  break;
240  }
241  else if ((Value != MaxY) && (ValueTwo != MaxY))
242  {
243  return false;
244  }
245  }
246  a_Scratch.XZM = Z2 + 1;
247 
248  return (FoundFrameZP && FoundFrameZM);
249  }
250 
251  virtual bool DoesIgnoreBuildCollision(const cWorld & a_World, const cItem & a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
252  {
253  return true;
254  }
255 
256  virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
257  {
258  UNUSED(a_Meta);
259  return 15;
260  }
261 };
262 
263 
264 
265 
@ E_BLOCK_NETHER_PORTAL
Definition: BlockType.h:105
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_FIRE
Definition: BlockType.h:61
@ E_BLOCK_OBSIDIAN
Definition: BlockType.h:59
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
@ dimEnd
Definition: Defines.h:234
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc.
Definition: Defines.h:38
Byte ColourID
Definition: Globals.h:162
#define UNUSED
Definition: Globals.h:72
virtual bool DoesIgnoreBuildCollision(const cWorld &a_World, const cItem &a_HeldItem, const Vector3i a_Position, const NIBBLETYPE a_Meta, const eBlockFace a_ClickedBlockFace, const bool a_ClickedDirectly) const override
Checks if the player can build "inside" this block.
Definition: BlockFire.h:251
static bool FindPortalSliceZ(int X, int Y, int Z1, int Z2, int MaxY, cChunkInterface &a_ChunkInterface, Scratch &a_Scratch)
Evaluates if coords are a portal going ZP / ZM; returns true if so, and writes boundaries to variable...
Definition: BlockFire.h:213
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
Returns the base colour ID of the block, as will be represented on a map, as per documentation: https...
Definition: BlockFire.h:256
static void FindAndSetPortalFrame(int X, int Y, int Z, cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, Scratch &a_Scratch)
Finds entire frame in any direction with the coordinates of a base block and fills hole with nether p...
Definition: BlockFire.h:124
static bool FindPortalSliceX(int X1, int X2, int Y, int Z, int MaxY, cChunkInterface &a_ChunkInterface, Scratch &a_Scratch)
Evaluates if coordinates are a portal going XP / XM; returns true if so, and writes boundaries to var...
Definition: BlockFire.h:174
static int FindObsidianCeiling(int X, int Y, int Z, cChunkInterface &a_ChunkInterface, int MaxY=0)
Traces along YP until it finds an obsidian block, returns Y difference or 0 if no portal,...
Definition: BlockFire.h:70
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem *const a_Tool) const override
Returns the pickups that would result if the block was mined by a_Digger using a_Tool.
Definition: BlockFire.h:61
static bool EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cChunkInterface &a_ChunkInterface)
Evaluates if coords have a valid border on top, based on MaxY.
Definition: BlockFire.h:105
virtual void OnPlaced(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) const override
Called by cWorld::SetBlock() after the block has been set.
Definition: BlockFire.h:30
int XZP
Portal boundary and direction variables.
Definition: BlockFire.h:22
constexpr cBlockHandler(BLOCKTYPE a_BlockType)
Definition: BlockHandler.h:29
void SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Sets the block at the specified coords to the specified value.
BLOCKTYPE GetBlock(Vector3i a_Pos)
virtual int GetMaxNetherPortalWidth(void) const =0
virtual int GetMinNetherPortalWidth(void) const =0
Returns or sets the minumim or maximum netherportal width.
virtual int GetMinNetherPortalHeight(void) const =0
Returns or sets the minumim or maximum netherportal height.
virtual int GetMaxNetherPortalHeight(void) const =0
virtual eDimension GetDimension(void) const =0
static const int Height
Definition: ChunkDef.h:125
Definition: Item.h:37
This class bridges a vector of cItem for safe access via Lua.
Definition: Item.h:215
T x
Definition: Vector3.h:17
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
Definition: World.h:53