Cuberite
A lightweight, fast and extensible game server for Minecraft
FurnaceRecipe.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
3 
4 #include "FurnaceRecipe.h"
5 #include "Item.h"
6 
7 #include <fstream>
8 
9 #define FURNACE_RECIPE_FILE FILE_IO_PREFIX "furnace.txt"
10 
11 
12 
13 
14 
15 typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
16 typedef std::list<cFurnaceRecipe::cFuel> FuelList;
17 
18 
19 
20 
21 
23 {
26 };
27 
28 
29 
30 
31 
34 {
35  ReloadRecipes();
36 }
37 
38 
39 
40 
41 
43 {
44  ClearRecipes();
45  delete m_pState;
46  m_pState = nullptr;
47 }
48 
49 
50 
51 
52 
54 {
55  ClearRecipes();
56  LOGD("Loading furnace recipes...");
57 
58  std::ifstream f(FURNACE_RECIPE_FILE, std::ios::in);
59  if (!f.good())
60  {
61  LOG("Could not open the furnace recipes file \"%s\". No furnace recipes are available.", FURNACE_RECIPE_FILE);
62  return;
63  }
64 
65  unsigned int LineNum = 0;
66  AString ParsingLine;
67 
68  while (std::getline(f, ParsingLine))
69  {
70  LineNum++;
71 
72  // Remove comments from the line:
73  size_t FirstCommentSymbol = ParsingLine.find('#');
74  if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
75  {
76  ParsingLine.erase(ParsingLine.begin() + static_cast<const long>(FirstCommentSymbol), ParsingLine.end());
77  }
78 
79  if (IsOnlyWhitespace(ParsingLine))
80  {
81  // Ignore empty and whitespace only lines
82  continue;
83  }
84 
85  switch (ParsingLine[0])
86  {
87  case '#':
88  {
89  // Comment
90  break;
91  }
92 
93  case '!':
94  {
95  AddFuelFromLine(ParsingLine, LineNum);
96  break;
97  }
98 
99  default:
100  {
101  AddRecipeFromLine(ParsingLine, LineNum);
102  break;
103  }
104  } // switch (ParsingLine[0])
105  } // while (getline(ParsingLine))
106 
107  LOG("Loaded %zu furnace recipes and %zu fuels", m_pState->Recipes.size(), m_pState->Fuel.size());
108 }
109 
110 
111 
112 
113 
114 void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum)
115 {
116  AString Line(a_Line);
117  Line.erase(Line.begin()); // Remove the beginning "!"
118  Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
119 
120  std::unique_ptr<cItem> Item = cpp14::make_unique<cItem>();
121  int BurnTime;
122 
123  const AStringVector & Sides = StringSplit(Line, "=");
124  if (Sides.size() != 2)
125  {
126  LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %zu", a_LineNum, Sides.size() - 1);
127  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
128  return;
129  }
130 
131  if (!ParseItem(Sides[0], *Item))
132  {
133  LOGWARNING("furnace.txt: line %d: Cannot parse item \"%s\".", a_LineNum, Sides[0].c_str());
134  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
135  return;
136  }
137 
138  if (!StringToInteger<int>(Sides[1], BurnTime))
139  {
140  LOGWARNING("furnace.txt: line %d: Cannot parse burn time.", a_LineNum);
141  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
142  return;
143  }
144 
145  // Add to fuel list:
146  cFuel Fuel;
147  Fuel.In = Item.release();
148  Fuel.BurnTime = BurnTime;
149  m_pState->Fuel.push_back(Fuel);
150 }
151 
152 
153 
154 
155 
156 void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum)
157 {
158  AString Line(a_Line);
159  Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
160 
161  int CookTime = 200;
162  float Reward = 0;
163  std::unique_ptr<cItem> InputItem = cpp14::make_unique<cItem>();
164  std::unique_ptr<cItem> OutputItem = cpp14::make_unique<cItem>();
165 
166  const AStringVector & Sides = StringSplit(Line, "=");
167  if (Sides.size() != 2)
168  {
169  LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %zu", a_LineNum, Sides.size() - 1);
170  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
171  return;
172  }
173 
174  const AStringVector & InputSplit = StringSplit(Sides[0], "@");
175  if (!ParseItem(InputSplit[0], *InputItem))
176  {
177  LOGWARNING("furnace.txt: line %d: Cannot parse input item \"%s\".", a_LineNum, InputSplit[0].c_str());
178  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
179  return;
180  }
181 
182  if (InputSplit.size() > 1)
183  {
184  if (!StringToInteger<int>(InputSplit[1], CookTime))
185  {
186  LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str());
187  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
188  return;
189  }
190  }
191  const AStringVector & OutputSplit = StringSplit(Sides[1], "$");
192  if (!ParseItem(OutputSplit[0], *OutputItem))
193  {
194  LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, OutputSplit[0].c_str());
195  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
196  return;
197  }
198  if (OutputSplit.size() > 1)
199  {
200  if (!StringToFloat(OutputSplit[1], Reward))
201  {
202  LOGWARNING("furnace.txt: line %d: Cannot parse reward \"%s\".", a_LineNum, OutputSplit[1].c_str());
203  LOGINFO("Offending line: \"%s\"", a_Line.c_str());
204  return;
205  }
206  }
207  cRecipe Recipe;
208  Recipe.In = InputItem.release();
209  Recipe.Out = OutputItem.release();
210  Recipe.CookTime = CookTime;
211  Recipe.Reward = Reward;
212  m_pState->Recipes.push_back(Recipe);
213 }
214 
215 
216 
217 
218 
219 bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
220 {
221  AString ItemString = a_String;
222 
223  const AStringVector & SplitAmount = StringSplit(ItemString, ",");
224  ItemString = SplitAmount[0];
225 
226  const AStringVector & SplitMeta = StringSplit(ItemString, ":");
227  ItemString = SplitMeta[0];
228 
229  if (!StringToItem(ItemString, a_Item))
230  {
231  return false;
232  }
233 
234  if (SplitAmount.size() > 1)
235  {
236  if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
237  {
238  return false;
239  }
240  }
241 
242  if (SplitMeta.size() > 1)
243  {
244  if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
245  {
246  return false;
247  }
248  }
249  return true;
250 }
251 
252 
253 
254 
255 
257 {
258  for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
259  {
260  cRecipe Recipe = *itr;
261  delete Recipe.In;
262  Recipe.In = nullptr;
263  delete Recipe.Out;
264  Recipe.Out = nullptr;
265  }
266  m_pState->Recipes.clear();
267 
268  for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
269  {
270  cFuel Fuel = *itr;
271  delete Fuel.In;
272  Fuel.In = nullptr;
273  }
274  m_pState->Fuel.clear();
275 }
276 
277 
278 
279 
280 
282 {
283  const cRecipe * BestRecipe = nullptr;
284  for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
285  {
286  const cRecipe & Recipe = *itr;
287  if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount))
288  {
289  if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount))
290  {
291  continue;
292  }
293  else
294  {
295  BestRecipe = &Recipe;
296  }
297  }
298  }
299  return BestRecipe;
300 }
301 
302 
303 
304 
305 
306 bool cFurnaceRecipe::IsFuel(const cItem & a_Item) const
307 {
308  for (auto & Fuel : m_pState->Fuel)
309  {
310  if ((Fuel.In->m_ItemType == a_Item.m_ItemType) && (Fuel.In->m_ItemCount <= a_Item.m_ItemCount))
311  {
312  return true;
313  }
314  }
315  return false;
316 }
317 
318 
319 
320 
321 
322 int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
323 {
324  int BestFuel = 0;
325  for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
326  {
327  const cFuel & Fuel = *itr;
328  if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount))
329  {
330  if (BestFuel > 0 && (BestFuel > Fuel.BurnTime))
331  {
332  continue;
333  }
334  else
335  {
336  BestFuel = Fuel.BurnTime;
337  }
338  }
339  }
340  return BestFuel;
341 }
342 
343 
344 
345 
int BurnTime
How long this fuel burns, in ticks.
Definition: FurnaceRecipe.h:25
float Reward
Experience reward for creating 1 of this item.
Definition: FurnaceRecipe.h:33
void ClearRecipes(void)
bool StringToFloat(const AString &a_String, float &a_Num)
Converts a string into a float.
#define FURNACE_RECIPE_FILE
short m_ItemDamage
Definition: Item.h:211
void ReloadRecipes(void)
void AddRecipeFromLine(const AString &a_Line, unsigned int a_LineNum)
Parses the recipe contained in the line, adds it to m_pState&#39;s recipes.
sFurnaceRecipeState * m_pState
Definition: FurnaceRecipe.h:59
bool IsFuel(const cItem &a_Item) const
Returns true if the item is a fuel, false if not.
bool IsOnlyWhitespace(const AString &a_String)
Returns true if only whitespace characters are present in the string.
std::vector< AString > AStringVector
Definition: StringUtils.h:14
int CookTime
How long this recipe takes to smelt, in ticks.
Definition: FurnaceRecipe.h:32
char m_ItemCount
Definition: Item.h:210
void LOGINFO(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:165
int GetBurnTime(const cItem &a_Fuel) const
Returns the amount of time that the specified fuel burns, in ticks.
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
bool ParseItem(const AString &a_String, cItem &a_Item)
Parses an item string in the format "<ItemType>[: <Damage>][, <Amount>]", returns true if successful...
#define LOGD(...)
Definition: LoggerSimple.h:40
void AddFuelFromLine(const AString &a_Line, unsigned int a_LineNum)
Parses the fuel contained in the line, adds it to m_pState&#39;s fuels.
std::string AString
Definition: StringUtils.h:13
std::list< cFurnaceRecipe::cRecipe > RecipeList
void LOG(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:156
AStringVector StringSplit(const AString &str, const AString &delim)
Split the string at any of the listed delimiters.
Definition: StringUtils.cpp:76
short m_ItemType
Definition: Item.h:209
std::list< cFurnaceRecipe::cFuel > FuelList
bool StringToItem(const AString &a_ItemTypeString, cItem &a_Item)
Translates an itemtype string into an item.
Definition: BlockID.cpp:238
Definition: Item.h:36
const cRecipe * GetRecipeFrom(const cItem &a_Ingredient) const
Returns a recipe for the specified input, nullptr if no recipe found.