diff options
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 2 | ||||
-rw-r--r-- | src/BlockEntities/FurnaceEntity.h | 2 | ||||
-rw-r--r-- | src/FurnaceRecipe.cpp | 241 | ||||
-rw-r--r-- | src/FurnaceRecipe.h | 33 |
4 files changed, 119 insertions, 159 deletions
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index b2c57e52d..adf10a72f 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -2663,7 +2663,7 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S) // Get the recipe for the input cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); - const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input); + const cFurnaceRecipe::cRecipe * Recipe = FR->GetRecipeFrom(*Input); if (Recipe == NULL) { // There is no such furnace recipe for this input, return no value diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index 4f935a74b..cf1a755e0 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -105,7 +105,7 @@ protected: NIBBLETYPE m_BlockMeta; /// The recipe for the current input slot - const cFurnaceRecipe::Recipe * m_CurrentRecipe; + const cFurnaceRecipe::cRecipe * m_CurrentRecipe; /// The item that is being smelted cItem m_LastInput; diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp index ab772e97b..eb64e0a91 100644 --- a/src/FurnaceRecipe.cpp +++ b/src/FurnaceRecipe.cpp @@ -12,8 +12,8 @@ -typedef std::list< cFurnaceRecipe::Recipe > RecipeList; -typedef std::list< cFurnaceRecipe::Fuel > FuelList; +typedef std::list<cFurnaceRecipe::cRecipe> RecipeList; +typedef std::list<cFurnaceRecipe::cFuel> FuelList; @@ -30,7 +30,7 @@ struct cFurnaceRecipe::sFurnaceRecipeState cFurnaceRecipe::cFurnaceRecipe() - : m_pState( new sFurnaceRecipeState) + : m_pState(new sFurnaceRecipeState) { ReloadRecipes(); } @@ -68,12 +68,18 @@ void cFurnaceRecipe::ReloadRecipes(void) while (std::getline(f, ParsingLine)) { LineNum++; - ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line if (ParsingLine.empty()) { continue; } + // Remove comments from the line: + size_t FirstCommentSymbol = ParsingLine.find('#'); + if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0)) + { + ParsingLine.erase(ParsingLine.begin() + FirstCommentSymbol, ParsingLine.end()); + } + switch (ParsingLine[0]) { case '#': @@ -105,157 +111,130 @@ void cFurnaceRecipe::ReloadRecipes(void) void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, int a_LineNum) { - // Fuel - int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0; - AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang) - - if ( - !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID - !ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health) - !ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value - ) + AString Line(a_Line); + Line.erase(Line.begin()); // Remove the beginning "!" + Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end()); + + cItem * Item = new cItem(); + int BurnTime; + + const AStringVector & Sides = StringSplit(Line, "="); + if (Sides.size() != 2) { + LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); return; } - // Add to fuel list: - Fuel F; - F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth); - F.BurnTime = IBurnTime; - m_pState->Fuel.push_back(F); -} - - - - + if (!ParseItem(Sides[0], *Item)) + { + LOGWARNING("furnace.txt: line %d: Cannot parse item \"%s\".", a_LineNum, Sides[0].c_str()); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } -void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum) -{ - int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0; - int OItemID = 0, OItemCount = 0, OItemHealth = 0; - AString::size_type BeginPos = 0; // Begin at start of line - - if ( - !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID - !ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health) - !ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time - !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID - !ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value - ) + if (!StringToInteger<int>(Sides[1], BurnTime)) { + LOGWARNING("furnace.txt: line %d: Cannot parse burn time.", a_LineNum); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); return; } - // Add to recipe list - Recipe R; - R.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth); - R.Out = new cItem((ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth); - R.CookTime = IBurnTime; - m_pState->Recipes.push_back(R); + // Add to fuel list: + cFuel Fuel; + Fuel.In = Item; + Fuel.BurnTime = BurnTime; + m_pState->Fuel.push_back(Fuel); } -void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing) +void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum) { - LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str()); -} - - + AString Line(a_Line); + Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end()); + int CookTime = 200; + cItem * InputItem = new cItem(); + cItem * OutputItem = new cItem(); + const AStringVector & Sides = StringSplit(Line, "="); + if (Sides.size() != 2) + { + LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; + } -bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue) -{ - // TODO: replace atoi with std::stoi - AString::size_type End; - if (a_IsLastValue) + const AStringVector & InputSplit = StringSplit(Sides[0], "@"); + if (!ParseItem(InputSplit[0], *InputItem)) { - End = a_Text.find_first_not_of(a_Delimiter, a_Begin); + LOGWARNING("furnace.txt: line %d: Cannot parse input item \"%s\".", a_LineNum, InputSplit[0].c_str()); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; } - else + + if (InputSplit.size() > 1) { - End = a_Text.find_first_of(a_Delimiter, a_Begin); - if (End == AString::npos) + if (!StringToInteger<int>(InputSplit[1], CookTime)) { - PrintParseError(a_Line, a_Begin, a_Delimiter); - return false; + LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str()); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; } } - - // stoi won't throw an exception if the string is alphanumeric, we should check for this - if (!DoesStringContainOnlyNumbers(a_Text.substr(a_Begin, End - a_Begin))) + + if (!ParseItem(Sides[1], *OutputItem)) { - PrintParseError(a_Line, a_Begin, "number"); - return false; + LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str()); + LOGINFO("Offending line: \"%s\"", a_Line.c_str()); + return; } - a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str()); - a_Begin = End + 1; // Jump over delimiter - return true; + cRecipe Recipe; + Recipe.In = InputItem; + Recipe.Out = OutputItem; + Recipe.CookTime = CookTime; + m_pState->Recipes.push_back(Recipe); } -bool cFurnaceRecipe::ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue) +bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item) { - // TODO: replace atoi with std::stoi - AString::size_type End, Begin = a_Begin; - - End = a_Text.find_first_of(a_DelimiterOne, Begin); - if (End != AString::npos) - { - if (DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin))) - { - a_ValueOne = std::atoi(a_Text.substr(Begin, End - Begin).c_str()); - Begin = End + 1; + AString ItemString = a_String; + + const AStringVector & SplitAmount = StringSplit(ItemString, ","); + ItemString = SplitAmount[0]; - if (a_IsLastValue) - { - End = a_Text.find_first_not_of(a_DelimiterTwo, Begin); - } - else - { - End = a_Text.find_first_of(a_DelimiterTwo, Begin); - if (End == AString::npos) - { - PrintParseError(a_Line, Begin, a_DelimiterTwo); - return false; - } - } + const AStringVector & SplitMeta = StringSplit(ItemString, ":"); + ItemString = SplitMeta[0]; - // stoi won't throw an exception if the string is alphanumeric, we should check for this - if (!DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin))) - { - PrintParseError(a_Line, Begin, "number"); - return false; - } - a_ValueTwo = atoi(a_Text.substr(Begin, End - Begin).c_str()); + if (!StringToItem(ItemString, a_Item)) + { + return false; + } - a_Begin = End + 1; // Jump over delimiter - return true; - } - else + if (SplitAmount.size() > 1) + { + if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount)) { - return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue); + return false; } } - - return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue); -} - - - - -bool cFurnaceRecipe::DoesStringContainOnlyNumbers(const AString & a_String) -{ - // TODO: replace this with std::all_of(a_String.begin(), a_String.end(), isdigit) - return (a_String.find_first_not_of("0123456789") == AString::npos); + if (SplitMeta.size() > 1) + { + if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage)) + { + return false; + } + } + return true; } @@ -266,19 +245,19 @@ void cFurnaceRecipe::ClearRecipes(void) { for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) { - Recipe R = *itr; - delete R.In; - R.In = NULL; - delete R.Out; - R.Out = NULL; + cRecipe Recipe = *itr; + delete Recipe.In; + Recipe.In = NULL; + delete Recipe.Out; + Recipe.Out = NULL; } m_pState->Recipes.clear(); for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) { - Fuel F = *itr; - delete F.In; - F.In = NULL; + cFuel Fuel = *itr; + delete Fuel.In; + Fuel.In = NULL; } m_pState->Fuel.clear(); } @@ -287,21 +266,21 @@ void cFurnaceRecipe::ClearRecipes(void) -const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const +const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const { - const Recipe * BestRecipe = 0; + const cRecipe * BestRecipe = 0; for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr) { - const Recipe & R = *itr; - if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount)) + const cRecipe & Recipe = *itr; + if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount)) { - if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount)) + if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount)) { continue; } else { - BestRecipe = &R; + BestRecipe = &Recipe; } } } @@ -317,16 +296,16 @@ int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const int BestFuel = 0; for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr) { - const Fuel & F = *itr; - if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount)) + const cFuel & Fuel = *itr; + if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount)) { - if (BestFuel > 0 && (BestFuel > F.BurnTime)) + if (BestFuel > 0 && (BestFuel > Fuel.BurnTime)) { continue; } else { - BestFuel = F.BurnTime; + BestFuel = Fuel.BurnTime; } } } diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h index 77ed35a57..de3ee56a0 100644 --- a/src/FurnaceRecipe.h +++ b/src/FurnaceRecipe.h @@ -19,23 +19,23 @@ public: void ReloadRecipes(void); - struct Fuel + struct cFuel { cItem * In; int BurnTime; ///< How long this fuel burns, in ticks }; - struct Recipe + struct cRecipe { cItem * In; cItem * Out; int CookTime; ///< How long this recipe takes to smelt, in ticks }; - /// Returns a recipe for the specified input, NULL if no recipe found - const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const; + /** Returns a recipe for the specified input, NULL if no recipe found */ + const cRecipe * GetRecipeFrom(const cItem & a_Ingredient) const; - /// Returns the amount of time that the specified fuel burns, in ticks + /** Returns the amount of time that the specified fuel burns, in ticks */ int GetBurnTime(const cItem & a_Fuel) const; private: @@ -49,27 +49,8 @@ private: Logs a warning to the console on input error. */ void AddRecipeFromLine(const AString & a_Line, int a_LineNum); - /** Calls LOGWARN with the line, position, and error */ - static void PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing); - - /** Reads a number from a string given, starting at a given position and ending at a delimiter given - Updates beginning position to the delimiter found + 1, and updates the value to the one read - If it encounters a substring that is not fully numeric, it will call SetParseError() and return false; the caller should abort processing - Otherwise, the function will return true - */ - static bool ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue = false); - - /** Reads two numbers from a string given, starting at a given position and ending at the first delimiter given, then again (with an updated position) until the second delimiter given - Updates beginning position to the second delimiter found + 1, and updates the values to the ones read - If it encounters a substring that is not fully numeric whilst reading the second value, it will call SetParseError() and return false; the caller should abort processing - If this happens whilst reading the first value, it will call ReadMandatoryNumber() with the appropriate position, as this may legitimately occur with the optional value and AString::find_first_of finding the incorrect delimiter. It will return the result of ReadMandatoryNumber() - True will be returned definitively for an optional value that is valid - */ - static bool ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue = false); - - /** Uses std::all_of to determine if a string contains only digits */ - static bool DoesStringContainOnlyNumbers(const AString & a_String); - + /** Parses an item string in the format "<ItemType>[^<Damage>][,<Amount>]", returns true if successful. */ + bool ParseItem(const AString & a_String, cItem & a_Item); struct sFurnaceRecipeState; sFurnaceRecipeState * m_pState; |