Cuberite
A lightweight, fast and extensible game server for Minecraft
PieceModifier.cpp
Go to the documentation of this file.
1 
2 // PieceModifier.cpp
3 
4 // Implements the various classes descending from cPiece::cPieceModifier
5 
6 #include "Globals.h"
7 #include "PieceModifier.h"
8 #include "../Noise/Noise.h"
9 
10 
11 
12 
13 
14 // Constant that is added to seed
15 static const int SEED_OFFSET = 135 * 13;
16 
17 
18 
20 
23 {
24 public:
26  m_Seed()
27  {
28  }
29 
30  virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) override
31  {
32  m_AllWeights = 0;
33  AString Params = a_Params;
34 
35 
37  auto idxPipe = Params.find('|');
38  if (idxPipe != AString::npos)
39  {
40  AString blocksToReplaceStr = Params.substr(0, idxPipe);
41  auto blocksToReplace = StringSplitAndTrim(blocksToReplaceStr, ",");
42  for (size_t i = 0; i < blocksToReplace.size(); i++)
43  {
44  BLOCKTYPE blockType = static_cast<BLOCKTYPE>(BlockStringToType(blocksToReplace[i]));
45  if ((blockType == E_BLOCK_AIR) && !NoCaseCompare(blocksToReplace[i], "Air"))
46  {
47  CONDWARNING(a_LogWarnings, "Cannot parse block type from string \"%s\"!", blocksToReplace[i].c_str());
48  return false;
49  }
50  m_BlocksToReplace[blockType] = 1;
51  }
52 
53  Params = Params.substr(idxPipe + 1, Params.length() - 1);
54  }
55  if (m_BlocksToReplace.size() == 0)
56  {
57  CONDWARNING(a_LogWarnings, "You must specify at least one block to replace%s!", "");
58  return false;
59  }
60 
61 
63  auto idxSqBracketStart = Params.find('[');
64  auto idxSqBracketStartLast = Params.find_last_of('[');
65 
66  bool isMultiMeta = false;
67  if ((idxSqBracketStart != idxSqBracketStartLast) && (idxSqBracketStartLast != Params.length() - 1))
68  {
69  // Meta per block type
70  isMultiMeta = true;
71  }
72  else
73  {
74  auto idxSqBracketEnd = Params.find(']');
75  if ((idxSqBracketEnd == Params.length() - 1) && (idxSqBracketStart != AString::npos))
76  {
77  AString metaParamsStr = Params.substr(idxSqBracketStart + 1, Params.length() - idxSqBracketStart - 2);
78  std::array<int, 4> metaParamsInt;
79  if (!ParseMeta(metaParamsStr, metaParamsInt, a_LogWarnings))
80  {
81  return false;
82  }
83 
84  m_MinMeta = metaParamsInt[0];
85  m_MaxMeta = metaParamsInt[1];
86  m_MinNoiseMeta = metaParamsInt[2];
87  m_MaxNoiseMeta = metaParamsInt[3];
88 
89  Params = Params.substr(0, idxSqBracketStart);
90  }
91  }
92 
93 
94  // BlocksToRandomize parsing
95  auto BlocksToRandomize = StringSplitAndTrim(Params, ";");
96  for (size_t i = 0; i < BlocksToRandomize.size(); i++)
97  {
98  AString block = BlocksToRandomize[i];
99 
101 
102  if (isMultiMeta)
103  {
104  auto sqBrStart = block.find('[');
105  if (sqBrStart != AString::npos)
106  {
107  auto sqBrEnd = block.find(']');
108  if (sqBrEnd != block.size() - 1)
109  {
110  CONDWARNING(a_LogWarnings, "If present, block meta params must be at the end of block to randomize definition \"%s\"!", block.c_str());
111  return false;
112 
113  }
114  AString metaParamsStr = block.substr(sqBrStart + 1, block.size() - sqBrStart - 2);
115 
116  std::array<int, 4> metaParamsInt;
117  if (!ParseMeta(metaParamsStr, metaParamsInt, a_LogWarnings))
118  {
119  return false;
120  }
121 
122  Block.m_MinMeta = metaParamsInt[0];
123  Block.m_MaxMeta = metaParamsInt[1];
124  Block.m_MinNoiseMeta = metaParamsInt[2];
125  Block.m_MaxNoiseMeta = metaParamsInt[3];
126 
127  block = block.substr(0, sqBrStart);
128 
129  }
130  // No meta randomization for this block
131  }
132 
133  auto BlockParams = StringSplitAndTrim(block, ",");
134  if (BlockParams.size() < 2)
135  {
136  CONDWARNING(a_LogWarnings, "Block weight is required param \"%s\"!", BlockParams[0].c_str());
137  return false;
138  }
139 
140  BLOCKTYPE BlockType = static_cast<BLOCKTYPE>(BlockStringToType(BlockParams[0]));
141  int BlockWeight = 0;
142  if ((BlockType != E_BLOCK_AIR) && !NoCaseCompare(BlockParams[0], "Air"))
143  {
144  // Failed to parse block type
145  CONDWARNING(
146  a_LogWarnings, "Cannot parse block type from string \"%s\"!",
147  BlockParams[0].c_str());
148  return false;
149  }
150 
151  if (!StringToInteger(BlockParams[1], BlockWeight))
152  {
153  // Failed to parse the crop weight:
154  CONDWARNING(
155  a_LogWarnings,
156  "Cannot parse block weight from string \"%s\"!",
157  BlockParams[1].c_str());
158  return false;
159  }
160 
161 
162  Block.m_Type = BlockType;
163  Block.m_Weight = BlockWeight;
164  m_AllWeights += BlockWeight;
165 
166  m_BlocksToRandomize.push_back(Block);
167  }
168  if (m_BlocksToRandomize.size() == 0)
169  {
170  CONDWARNING(a_LogWarnings, "You must specify at least one block to randomize%s!", "");
171  return false;
172  }
173 
174  return true;
175  }
176 
177 
178 
179 
180 
181  bool ParseMeta(const AString & a_Meta, std::array<int, 4> & a_ParsedMeta, bool a_LogWarnings)
182  {
183  auto MetaParams = StringSplitAndTrim(a_Meta, ",");
184 
185  for (size_t i = 0; i < MetaParams.size(); i++)
186  {
187  int Value;
188  if (!StringToInteger(MetaParams[i], Value))
189  {
190  // Failed to parse meta parameter from string:
191  CONDWARNING(a_LogWarnings, "Cannot parse meta param from string \"%s\", meta: %s!", MetaParams[i].c_str(), a_Meta.c_str());
192  return false;
193  }
194 
195  if (i > 3)
196  {
197  CONDWARNING(a_LogWarnings, "Unsupported meta param \"%d\"!", Value);
198  return false;
199  }
200 
201  a_ParsedMeta[i] = Value;
202  }
203 
204  if (MetaParams.size() == 1)
205  {
206  // Noise is not used for meta
207  a_ParsedMeta[1] = a_ParsedMeta[0];
208  }
209  else if (MetaParams.size() == 2)
210  {
211  // Noise range is same as meta range
212  a_ParsedMeta[2] = a_ParsedMeta[0];
213  a_ParsedMeta[3] = a_ParsedMeta[1];
214  }
215  else if (MetaParams.size() == 3)
216  {
217  a_ParsedMeta[3] = a_ParsedMeta[1];
218  }
219 
220  return true;
221  }
222 
223 
224 
225 
226 
227  virtual void Modify(cBlockArea & a_Image, const Vector3i a_PiecePos, const int a_PieceRot) override
228  {
229  cNoise Noise(m_Seed);
230  cNoise PieceNoise(Noise.IntNoise3DInt(a_PiecePos));
231 
232  size_t NumBlocks = a_Image.GetBlockCount();
233  BLOCKTYPE * BlockTypes = a_Image.GetBlockTypes();
234  BLOCKTYPE * BlockMetas = a_Image.GetBlockMetas();
235 
236  for (size_t i = 0; i < NumBlocks; i++)
237  {
238  if (m_BlocksToReplace.count(BlockTypes[i]))
239  {
240  float BlockRnd = PieceNoise.IntNoise2DInRange(a_PieceRot, static_cast<int>(i), 0.0F, static_cast<float>(m_AllWeights));
241 
242  int weightDelta = 0;
243  for (auto & blockToRnd : m_BlocksToRandomize)
244  {
245  weightDelta += blockToRnd.m_Weight;
246  if (BlockRnd <= weightDelta)
247  {
248  BlockTypes[i] = blockToRnd.m_Type;
249 
250  // Per block meta params
251  if (blockToRnd.m_MinMeta < blockToRnd.m_MaxMeta)
252  {
253  int BlockMetaRnd = std::clamp(static_cast<int>(PieceNoise.IntNoise2DInRange(a_PieceRot*2, static_cast<int>(i), static_cast<float>(blockToRnd.m_MinNoiseMeta), static_cast<float>(blockToRnd.m_MaxNoiseMeta))), blockToRnd.m_MinMeta, blockToRnd.m_MaxMeta);
254  BlockMetas[i] = static_cast<NIBBLETYPE>(BlockMetaRnd);
255  }
256  else if ((blockToRnd.m_MaxMeta > -1) && (blockToRnd.m_MaxMeta == blockToRnd.m_MinMeta))
257  {
258  // Change meta if at least minimum meta was specified
259  BlockMetas[i] = static_cast<NIBBLETYPE>(blockToRnd.m_MaxMeta);
260  }
261  break;
262  }
263  }
264 
265  // All blocks meta params
266  if (m_MaxMeta > m_MinMeta)
267  {
268  int BlockMetaRnd = std::clamp(static_cast<int>(PieceNoise.IntNoise2DInRange(a_PieceRot * 2, static_cast<int>(i), static_cast<float>(m_MinNoiseMeta), static_cast<float>(m_MaxNoiseMeta))), m_MinMeta, m_MaxMeta);
269  BlockMetas[i] = static_cast<NIBBLETYPE>(BlockMetaRnd);
270  }
271  else if ((m_MaxMeta > -1) && (m_MaxMeta == m_MinMeta))
272  {
273  // Change meta if at least minimum meta was specified
274  BlockMetas[i] = static_cast<NIBBLETYPE>(m_MaxMeta);
275  }
276  }
277  } // for i - BlockTypes[]
278  }
279 
280  virtual void AssignSeed(int a_Seed) override
281  {
282  m_Seed = a_Seed + SEED_OFFSET;
283  }
284 protected:
285  int m_Seed;
286  int m_AllWeights = 0;
287 
288 
290  std::map<BLOCKTYPE, int> m_BlocksToReplace;
291 
294 
296  int m_MinMeta = 0;
297 
299  int m_MaxMeta = -1;
300 
302  int m_MaxNoiseMeta = 0;
303 
305  int m_MinNoiseMeta = 0;
306 };
307 
308 
309 
310 
311 
313 // CreatePieceModifierFromString:
314 
315 bool CreatePieceModifierFromString(const AString & a_Definition, std::shared_ptr<cPiece::cPieceModifiers> & a_Modifiers, bool a_LogWarnings)
316 {
317 
318  auto idxCurlyStart = a_Definition.find('{');
319  auto idxCurlyFirstEnd = a_Definition.find('}');
320  if ((idxCurlyStart == AString::npos) && (idxCurlyFirstEnd == AString::npos))
321  {
322  CONDWARNING(a_LogWarnings, "Piece metadata \"Modifiers\" needs at least one valid modifier definition \"%s\"!", a_Definition.c_str());
323  return false;
324  }
325 
326  auto modifiersStr = StringSplitAndTrim(a_Definition, "{");
327 
328  for (size_t i = 0; i < modifiersStr.size(); i++)
329  {
330  AString modifierStr = TrimString(modifiersStr[i]);
331 
332  if (modifierStr.size() == 0)
333  {
334  continue;
335  }
336  auto idxCurlyEnd = modifierStr.find('}');
337  if (idxCurlyEnd == AString::npos)
338  {
339  CONDWARNING(a_LogWarnings, "Modifier definition must end with curly bracket \"%s\"!!", modifierStr.c_str());
340  return false;
341  }
342 
343  modifierStr = modifierStr.substr(0, idxCurlyEnd);
344 
345  // Break apart the modifier class, the first parameter before the first pipe char:
346  auto idxPipe = modifierStr.find('|');
347  if (idxPipe == AString::npos)
348  {
349  idxPipe = modifierStr.length();
350  }
351  AString ModifierClass = modifierStr.substr(0, idxPipe);
352 
353  // Create a modifier class based on the class string:
354  cPiece::cPieceModifierPtr Modifier;
355  if (NoCaseCompare(ModifierClass, "RandomizeBlocks") == 0)
356  {
357  Modifier = std::make_shared<cPieceModifierRandomizeBlocks>();
358  }
359 
360  if (Modifier == nullptr)
361  {
362  CONDWARNING(a_LogWarnings, "Unknown modifier class \"%s\" %s!", ModifierClass.c_str(), modifierStr.c_str());
363  return false;
364  }
365 
366  // Initialize the modifier's parameters:
367  AString Params;
368  if (idxPipe < modifierStr.length())
369  {
370  Params = modifierStr.substr(idxPipe + 1);
371  }
372 
373  if (!Modifier->InitializeFromString(Params, a_LogWarnings))
374  {
375  CONDWARNING(a_LogWarnings, "InitializeFromString error \"%s\" -- %!", Params.c_str(), modifierStr.c_str());
376  return false;
377  }
378 
379  a_Modifiers->push_back(Modifier);
380  }
381 
382  return true;
383 }
384 
385 
386 
387 
int BlockStringToType(const AString &a_BlockTypeString)
Translates a blocktype string into blocktype.
Definition: BlockType.cpp:212
@ E_BLOCK_AIR
Definition: BlockType.h:10
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
bool CreatePieceModifierFromString(const AString &a_Definition, std::shared_ptr< cPiece::cPieceModifiers > &a_Modifiers, bool a_LogWarnings)
static const int SEED_OFFSET
std::vector< cRandomizedBlock > cRandomizedBlocks
Definition: PieceModifier.h:45
#define CONDWARNING(ShouldLog,...)
Definition: LoggerSimple.h:99
BlockType
Definition: BlockTypes.h:4
AStringVector StringSplitAndTrim(const AString &str, const AString &delim)
Split the string at any of the listed delimiters and trim each value.
AString TrimString(const AString &str)
Trims whitespace at both ends of the string.
int NoCaseCompare(const AString &s1, const AString &s2)
Case-insensitive string comparison.
std::string AString
Definition: StringUtils.h:11
bool StringToInteger(const AString &a_str, T &a_Num)
Parses any integer type.
Definition: StringUtils.h:143
NIBBLETYPE * GetBlockMetas(void) const
Definition: BlockArea.h:390
size_t GetBlockCount(void) const
Definition: BlockArea.h:393
BLOCKTYPE * GetBlockTypes(void) const
Returns the internal pointer to the block types.
Definition: BlockArea.h:389
A modifier which is pseudo-randomly replacing blocks to other types and metas.
int m_MaxMeta
Maximum meta to randomize.
int m_MinMeta
Minimum meta to randomize.
int m_MaxNoiseMeta
Maximum meta in noise range.
virtual bool InitializeFromString(const AString &a_Params, bool a_LogWarnings) override
Initializes the modifier's parameters from the string representation.
bool ParseMeta(const AString &a_Meta, std::array< int, 4 > &a_ParsedMeta, bool a_LogWarnings)
int m_MinNoiseMeta
Minimum meta in noise range.
virtual void AssignSeed(int a_Seed) override
Called when the piece pool is assigned to a generator, so that the modifiers can access world seed.
std::map< BLOCKTYPE, int > m_BlocksToReplace
Block types of a blocks which are being replaced by this strategy.
virtual void Modify(cBlockArea &a_Image, const Vector3i a_PiecePos, const int a_PieceRot) override
Called prior to writing piece to a chunk, so that modifiers can modify blocks in the blockarea.
cRandomizedBlocks m_BlocksToRandomize
Randomized blocks with their weights and meta params.
Used to store block type, meta, weight and some more params.
Definition: PieceModifier.h:26
std::shared_ptr< cPieceModifier > cPieceModifierPtr
Definition: PiecePool.h:174
Base class (interface) for piece modifiers.
Definition: PiecePool.h:153
Definition: Noise.h:20
int IntNoise3DInt(int a_X, int a_Y, int a_Z) const
Definition: Noise.h:254
NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
Definition: Noise.h:32