Cuberite
A lightweight, fast and extensible game server for Minecraft
BlockID.cpp
Go to the documentation of this file.
1 // BlockID.cpp
2 
3 // Implements the helper functions for converting Block ID string to int etc.
4 
5 #include "Globals.h"
6 #include "IniFile.h"
7 #include "Item.h"
8 #include "Mobs/Monster.h"
9 
10 
11 
12 
13 
15 {
16  // Making the map case-insensitive:
17  struct Comparator
18  {
19  bool operator ()(const AString & a_Item1, const AString & a_Item2) const
20  {
21  return (NoCaseCompare(a_Item1, a_Item2) > 0);
22  }
23  } ;
24 
25  typedef std::map<AString, std::pair<short, short>, Comparator> ItemMap;
26 
27 public:
28  static bool m_bHasRunInit;
29 
31  {
32  // Dont load items.ini on construct, this will search the wrong path when running as a service.
33  }
34 
35 
36  void init()
37  {
38  m_bHasRunInit = true;
39 
40  cIniFile Ini;
41  if (!Ini.ReadFile("items.ini"))
42  {
43  return;
44  }
45  int KeyID = Ini.FindKey("Items");
46  if (KeyID == cIniFile::noID)
47  {
48  return;
49  }
50  int NumValues = Ini.GetNumValues(KeyID);
51  for (int i = 0; i < NumValues; i++)
52  {
53  AString Name = Ini.GetValueName(KeyID, i);
54  if (Name.empty())
55  {
56  continue;
57  }
58  AString Value = Ini.GetValue(KeyID, i);
59  AddToMap(Name, Value);
60  } // for i - Ini.Values[]
61  }
62 
63 
64  int Resolve(const AString & a_ItemName)
65  {
66  ItemMap::iterator itr = m_Map.find(a_ItemName);
67  if (itr == m_Map.end())
68  {
69  return -1;
70  }
71  return itr->second.first;
72  }
73 
74 
75  bool ResolveItem(const AString & a_ItemName, cItem & a_Item)
76  {
77  // Split into parts divided by either ':' or '^'
78  AStringVector Split = StringSplitAndTrim(a_ItemName, ":^");
79  if (Split.empty())
80  {
81  return false;
82  }
83 
84  ItemMap::iterator itr = m_Map.find(Split[0]);
85  if (itr != m_Map.end())
86  {
87  // Resolved as a string, assign the type and the default damage / count
88  a_Item.m_ItemType = itr->second.first;
89  a_Item.m_ItemDamage = itr->second.second;
90  if (a_Item.m_ItemDamage == -1)
91  {
92  a_Item.m_ItemDamage = 0;
93  }
94  }
95  else
96  {
97  // Not a resolvable string, try pure numbers: "45:6", "45^6" etc.
98  if (!StringToInteger(Split[0], a_Item.m_ItemType))
99  {
100  // Parsing the number failed
101  return false;
102  }
103  }
104 
105  // Parse the damage, if present:
106  if (Split.size() < 2)
107  {
108  // Not present, set the item as valid and return success:
109  a_Item.m_ItemCount = 1;
110  return true;
111  }
112 
113  if (!StringToInteger(Split[1], a_Item.m_ItemDamage))
114  {
115  // Parsing the number failed
116  return false;
117  }
118  a_Item.m_ItemCount = 1;
119  return true;
120  }
121 
122 
123  AString Desolve(short a_ItemType, short a_ItemDamage)
124  {
125  // First try an exact match, both ItemType and ItemDamage ("birchplanks=5:2"):
126  for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr)
127  {
128  if ((itr->second.first == a_ItemType) && (itr->second.second == a_ItemDamage))
129  {
130  return itr->first;
131  }
132  } // for itr - m_Map[]
133 
134  // There is no exact match, try matching ItemType only ("planks=5"):
135  if (a_ItemDamage == 0)
136  {
137  for (ItemMap::iterator itr = m_Map.begin(), end = m_Map.end(); itr != end; ++itr)
138  {
139  if ((itr->second.first == a_ItemType) && (itr->second.second == -1))
140  {
141  return itr->first;
142  }
143  } // for itr - m_Map[]
144  }
145 
146  // No match at all, synthesize a string ("5:1"):
147  AString res;
148  if (a_ItemDamage == -1)
149  {
150  Printf(res, "%d", a_ItemType);
151  }
152  else
153  {
154  Printf(res, "%d:%d", a_ItemType, a_ItemDamage);
155  }
156  return res;
157  }
158 
159 
160 protected:
161  ItemMap m_Map;
162 
163 
164  void AddToMap(const AString & a_Name, const AString & a_Value)
165  {
166  AStringVector Split = StringSplit(a_Value, ":");
167  if (Split.size() == 1)
168  {
169  Split = StringSplit(a_Value, "^");
170  }
171  if (Split.empty())
172  {
173  return;
174  }
175  short ItemType;
176  if (!StringToInteger(Split[0], ItemType))
177  {
178  ASSERT(!"Invalid item type");
179  }
180  short ItemDamage = -1;
181  if (Split.size() > 1 && !StringToInteger(Split[1], ItemDamage))
182  {
183  ASSERT(!"Invalid item damage");
184  }
185  m_Map[a_Name] = std::make_pair(ItemType, ItemDamage);
186  }
187 } ;
188 
189 
190 
191 
192 bool cBlockIDMap::m_bHasRunInit = false;
194 
195 
196 
197 
198 
199 /*
200 // Quick self-test:
201 class Tester
202 {
203 public:
204  Tester(void)
205  {
206  cItem Item;
207  gsBlockIDMap.ResolveItem("charcoal", Item);
208  AString Charcoal = gsBlockIDMap.Desolve(Item.m_ItemType, Item.m_ItemDamage);
209  ASSERT(Charcoal == "charcoal");
210  }
211 } test;
212 //*/
213 
214 
215 
216 
217 
218 int BlockStringToType(const AString & a_BlockTypeString)
219 {
220  int res = atoi(a_BlockTypeString.c_str());
221  if ((res != 0) || (a_BlockTypeString.compare("0") == 0))
222  {
223  // It was a valid number, return that
224  return res;
225  }
226 
227  if (!gsBlockIDMap.m_bHasRunInit)
228  {
229  gsBlockIDMap.init();
230  }
231  return gsBlockIDMap.Resolve(TrimString(a_BlockTypeString));
232 }
233 
234 
235 
236 
237 
238 bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item)
239 {
240  AString ItemName = TrimString(a_ItemTypeString);
241  if (ItemName.substr(0, 10) == "minecraft:")
242  {
243  ItemName = ItemName.substr(10);
244  }
245 
246  if (!gsBlockIDMap.m_bHasRunInit)
247  {
248  gsBlockIDMap.init();
249  }
250  return gsBlockIDMap.ResolveItem(ItemName, a_Item);
251 }
252 
253 
254 
255 
256 
257 AString ItemToString(const cItem & a_Item)
258 {
259  if (!gsBlockIDMap.m_bHasRunInit)
260  {
261  gsBlockIDMap.init();
262  }
263  return gsBlockIDMap.Desolve(a_Item.m_ItemType, a_Item.m_ItemDamage);
264 }
265 
266 
267 
268 
269 
270 AString ItemTypeToString(short a_ItemType)
271 {
272  if (!gsBlockIDMap.m_bHasRunInit)
273  {
274  gsBlockIDMap.init();
275  }
276  return gsBlockIDMap.Desolve(a_ItemType, -1);
277 }
278 
279 
280 
281 
282 
284 {
285  AString res;
286  Printf(res, "%s:%d * %d", ItemToString(a_Item).c_str(), a_Item.m_ItemDamage, a_Item.m_ItemCount);
287  return res;
288 }
289 
290 
291 
292 
293 
294 eDimension StringToDimension(const AString & a_DimensionString)
295 {
296  // First try decoding as a number
297  int res;
298  if (StringToInteger(a_DimensionString, res))
299  {
300  // It was a valid number
301  return static_cast<eDimension>(res);
302  }
303 
304  // Decode using a built-in map:
305  static struct
306  {
307  eDimension m_Dimension;
308  const char * m_String;
309  } DimensionMap [] =
310  {
311  { dimOverworld, "Overworld"},
312  { dimOverworld, "Normal"},
313  { dimOverworld, "World"},
314  { dimNether, "Nether"},
315  { dimNether, "Hell"}, // Alternate name for Nether
316  { dimEnd, "End"},
317  { dimEnd, "Sky"}, // Old name for End
318  } ;
319  for (size_t i = 0; i < ARRAYCOUNT(DimensionMap); i++)
320  {
321  if (NoCaseCompare(DimensionMap[i].m_String, a_DimensionString) == 0)
322  {
323  return DimensionMap[i].m_Dimension;
324  }
325  } // for i - DimensionMap[]
326 
327  // Not found
328  LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", a_DimensionString.c_str());
329  return dimOverworld;
330 }
331 
332 
333 
334 
335 
337 {
338  // Decode using a built-in map:
339  static struct
340  {
341  eDimension m_Dimension;
342  const char * m_String;
343  } DimensionMap[] =
344  {
345  { dimOverworld, "Overworld" },
346  { dimNether, "Nether" },
347  { dimEnd, "End" },
348  };
349 
350  for (size_t i = 0; i < ARRAYCOUNT(DimensionMap); i++)
351  {
352  if (DimensionMap[i].m_Dimension == a_Dimension)
353  {
354  return DimensionMap[i].m_String;
355  }
356  } // for i - DimensionMap[]
357 
358  // Not found
359  LOGWARNING("Unknown dimension: \"%i\". Setting to Overworld", static_cast<int>(a_Dimension));
360  return "Overworld";
361 }
362 
363 
364 
365 
366 
369 {
370  // Make sure to keep this alpha-sorted.
371  switch (a_DamageType)
372  {
373  case dtAdmin: return "dtAdmin";
374  case dtAttack: return "dtAttack";
375  case dtCactusContact: return "dtCactusContact";
376  case dtDrowning: return "dtDrowning";
377  case dtEnderPearl: return "dtEnderPearl";
378  case dtFalling: return "dtFalling";
379  case dtFireContact: return "dtFireContact";
380  case dtInVoid: return "dtInVoid";
381  case dtLavaContact: return "dtLavaContact";
382  case dtLightning: return "dtLightning";
383  case dtOnFire: return "dtOnFire";
384  case dtPoisoning: return "dtPoisoning";
385  case dtWithering: return "dtWithering";
386  case dtPotionOfHarming: return "dtPotionOfHarming";
387  case dtRangedAttack: return "dtRangedAttack";
388  case dtStarving: return "dtStarving";
389  case dtSuffocating: return "dtSuffocation";
390  case dtExplosion: return "dtExplosion";
391  }
392  UNREACHABLE("Unsupported damage type");
393 }
394 
395 
396 
397 
398 
401 eDamageType StringToDamageType(const AString & a_DamageTypeString)
402 {
403  // First try decoding as a number:
404  int res;
405  if (!StringToInteger(a_DamageTypeString, res))
406  {
407  // It was a valid number
408  return static_cast<eDamageType>(res);
409  }
410 
411  // Decode using a built-in map:
412  static struct
413  {
414  eDamageType m_DamageType;
415  const char * m_String;
416  } DamageTypeMap [] =
417  {
418  // Cannonical names:
419  { dtAttack, "dtAttack"},
420  { dtRangedAttack, "dtRangedAttack"},
421  { dtLightning, "dtLightning"},
422  { dtFalling, "dtFalling"},
423  { dtDrowning, "dtDrowning"},
424  { dtSuffocating, "dtSuffocation"},
425  { dtStarving, "dtStarving"},
426  { dtCactusContact, "dtCactusContact"},
427  { dtLavaContact, "dtLavaContact"},
428  { dtPoisoning, "dtPoisoning"},
429  { dtWithering, "dtWithering"},
430  { dtOnFire, "dtOnFire"},
431  { dtFireContact, "dtFireContact"},
432  { dtInVoid, "dtInVoid"},
433  { dtPotionOfHarming, "dtPotionOfHarming"},
434  { dtAdmin, "dtAdmin"},
435  { dtExplosion, "dtExplosion"},
436 
437  // Common synonyms:
438  { dtAttack, "dtPawnAttack"},
439  { dtAttack, "dtEntityAttack"},
440  { dtAttack, "dtMob"},
441  { dtAttack, "dtMobAttack"},
442  { dtRangedAttack, "dtArrowAttack"},
443  { dtRangedAttack, "dtArrow"},
444  { dtRangedAttack, "dtProjectile"},
445  { dtFalling, "dtFall"},
446  { dtDrowning, "dtDrown"},
447  { dtSuffocating, "dtSuffocation"},
448  { dtStarving, "dtStarvation"},
449  { dtStarving, "dtHunger"},
450  { dtCactusContact, "dtCactus"},
451  { dtCactusContact, "dtCactuses"},
452  { dtCactusContact, "dtCacti"},
453  { dtLavaContact, "dtLava"},
454  { dtPoisoning, "dtPoison"},
455  { dtWithering, "dtWither"},
456  { dtOnFire, "dtBurning"},
457  { dtFireContact, "dtInFire"},
458  { dtAdmin, "dtPlugin"},
459  } ;
460  for (size_t i = 0; i < ARRAYCOUNT(DamageTypeMap); i++)
461  {
462  if (NoCaseCompare(DamageTypeMap[i].m_String, a_DamageTypeString) == 0)
463  {
464  return DamageTypeMap[i].m_DamageType;
465  }
466  } // for i - DamageTypeMap[]
467 
468  // Not found:
469  return static_cast<eDamageType>(-1);
470 }
471 
472 
473 
474 
475 
476 cItem GetIniItemSet(cIniFile & a_IniFile, const char * a_Section, const char * a_Key, const char * a_Default)
477 {
478  AString ItemStr = a_IniFile.GetValueSet(a_Section, a_Key, a_Default);
479  cItem res;
480  if (!StringToItem(ItemStr, res))
481  {
482  res.Empty();
483  }
484  return res;
485 }
486 
487 
488 
489 
490 
491 
eDimension
Dimension of a world.
Definition: BlockID.h:1127
static bool m_bHasRunInit
Definition: BlockID.cpp:28
int BlockStringToType(const AString &a_BlockTypeString)
Translates a blocktype string into blocktype.
Definition: BlockID.cpp:218
static cBlockIDMap gsBlockIDMap
Definition: BlockID.cpp:193
short m_ItemDamage
Definition: Item.h:211
bool StringToInteger(const AString &a_str, T &a_Num)
Parses any integer type.
Definition: StringUtils.h:161
const char * m_String
Definition: BiomeDef.cpp:15
cItem GetIniItemSet(cIniFile &a_IniFile, const char *a_Section, const char *a_Key, const char *a_Default)
Returns a cItem representing the item described in an IniFile&#39;s value; if the value doesn&#39;t exist...
Definition: BlockID.cpp:476
cBlockIDMap(void)
Definition: BlockID.cpp:30
int Resolve(const AString &a_ItemName)
Definition: BlockID.cpp:64
int GetNumValues(const AString &keyname) const
Definition: IniFile.cpp:318
AString ItemToString(const cItem &a_Item)
Translates a full item into a string.
Definition: BlockID.cpp:257
eDamageType StringToDamageType(const AString &a_DamageTypeString)
Translates a damage type string to damage type.
Definition: BlockID.cpp:401
AString Desolve(short a_ItemType, short a_ItemDamage)
Definition: BlockID.cpp:123
bool ReadFile(const AString &a_FileName, bool a_AllowExampleRedirect=true)
Reads the contents of the specified ini file If the file doesn&#39;t exist and a_AllowExampleRedirect is ...
Definition: IniFile.cpp:50
std::vector< AString > AStringVector
Definition: StringUtils.h:14
AString DamageTypeToString(eDamageType a_DamageType)
Translates damage type constant to a string representation (built-in)
Definition: BlockID.cpp:368
eDamageType
Damage type, used in the TakeDamageInfo structure and related functions.
Definition: BlockID.h:1140
int FindKey(const AString &keyname) const
Returns index of specified key, or noID if not found.
Definition: IniFile.cpp:239
void AddToMap(const AString &a_Name, const AString &a_Value)
Definition: BlockID.cpp:164
eDimension StringToDimension(const AString &a_DimensionString)
Translates a dimension string to dimension enum.
Definition: BlockID.cpp:294
int NoCaseCompare(const AString &s1, const AString &s2)
Case-insensitive string comparison.
AStringVector StringSplitAndTrim(const AString &str, const AString &delim)
Split the string at any of the listed delimiters and trim each value.
char m_ItemCount
Definition: Item.h:210
AString & Printf(AString &str, const char *format, fmt::ArgList args)
Output the formatted text into the string.
Definition: StringUtils.cpp:55
#define ASSERT(x)
Definition: Globals.h:335
AString DimensionToString(eDimension a_Dimension)
Translates a dimension enum to dimension string.
Definition: BlockID.cpp:336
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
void Empty(void)
Definition: Item.h:92
AString GetValueName(const AString &keyname, const int valueID) const
Definition: IniFile.cpp:345
bool ResolveItem(const AString &a_ItemName, cItem &a_Item)
Definition: BlockID.cpp:75
AString TrimString(const AString &str)
Trims whitespace at both ends of the string.
std::string AString
Definition: StringUtils.h:13
ItemMap m_Map
Definition: BlockID.cpp:161
AStringVector StringSplit(const AString &str, const AString &delim)
Split the string at any of the listed delimiters.
Definition: StringUtils.cpp:76
void init()
Definition: BlockID.cpp:36
AString ItemToFullString(const cItem &a_Item)
Translates a full item into a fully-specified string (including meta and count).
Definition: BlockID.cpp:283
short m_ItemType
Definition: Item.h:209
AString GetValue(const AString &keyname, const AString &valuename, const AString &defValue="") const override
Get the value at the specified key and value, returns defValue on failure.
Definition: IniFile.cpp:481
bool operator()(const AString &a_Item1, const AString &a_Item2) const
Definition: BlockID.cpp:19
bool StringToItem(const AString &a_ItemTypeString, cItem &a_Item)
Translates an itemtype string into an item.
Definition: BlockID.cpp:238
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
Definition: Globals.h:290
Definition: Item.h:36
AString ItemTypeToString(short a_ItemType)
Translates itemtype into a string.
Definition: BlockID.cpp:270
#define UNREACHABLE(x)
Use to mark code that should be impossible to reach.
Definition: Globals.h:344
std::map< AString, std::pair< short, short >, Comparator > ItemMap
Definition: BlockID.cpp:25
AString GetValueSet(const AString &keyname, const AString &valuename, const AString &defValue="") override
Gets the value; if not found, write the default to the repository.
Definition: IniFile.cpp:524