From 7440a07a53176f6ec987313d9c6bfe331ff98d53 Mon Sep 17 00:00:00 2001 From: BevapDin Date: Tue, 31 Dec 2019 16:45:22 +0100 Subject: [PATCH] Store crafting related item data in a separate object inside a value_ptr This reduces the overhead for non-craft items to a single pointer. --- src/crafting.cpp | 6 ++--- src/item.cpp | 43 +++++++++++++++++++-------------- src/item.h | 31 +++++++++++++++++------- src/savegame_json.cpp | 56 +++++++++++++++++++++++++++++++++---------- src/value_ptr.h | 21 ++++++++++++++++ 5 files changed, 115 insertions(+), 42 deletions(-) diff --git a/src/crafting.cpp b/src/crafting.cpp index 65e81f1b2aff0..530519df6a887 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -956,7 +956,7 @@ int item::get_next_failure_point() const debugmsg( "get_next_failure_point() called on non-craft '%s.' Aborting.", tname() ); return INT_MAX; } - return next_failure_point >= 0 ? next_failure_point : INT_MAX; + return craft_data_->next_failure_point >= 0 ? craft_data_->next_failure_point : INT_MAX; } void item::set_next_failure_point( const player &crafter ) @@ -969,7 +969,7 @@ void item::set_next_failure_point( const player &crafter ) const int percent_left = 10000000 - item_counter; const int failure_point_delta = crafter.crafting_success_roll( get_making() ) * percent_left; - next_failure_point = item_counter + failure_point_delta; + craft_data_->next_failure_point = item_counter + failure_point_delta; } static void destroy_random_component( item &craft, const player &crafter ) @@ -1036,7 +1036,7 @@ requirement_data item::get_continue_reqs() const debugmsg( "get_continue_reqs() called on non-craft '%s.' Aborting.", tname() ); return requirement_data(); } - return requirement_data::continue_requirements( comps_used, components ); + return requirement_data::continue_requirements( craft_data_->comps_used, components ); } void item::inherit_flags( const item &parent ) diff --git a/src/item.cpp b/src/item.cpp index 8a3e7a54bfd95..b2a0754892f2d 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -284,9 +284,10 @@ static const item *get_most_rotten_component( const item &craft ) item::item( const recipe *rec, int qty, std::list items, std::vector selections ) : item( "craft", calendar::turn, qty ) { - making = rec; + craft_data_ = cata::make_value(); + craft_data_->making = rec; components = items; - comps_used = selections; + craft_data_->comps_used = selections; if( is_food() ) { active = true; @@ -753,10 +754,10 @@ bool item::stacks_with( const item &rhs, bool check_components ) const if( corpse != nullptr && rhs.corpse != nullptr && corpse->id != rhs.corpse->id ) { return false; } - if( is_craft() && rhs.is_craft() ) { - if( get_making().ident() != rhs.get_making().ident() ) { - return false; - } + if( craft_data_ || rhs.craft_data_ ) { + // In-progress crafts are always distinct items. Easier to handle for the player, + // and there shouldn't be that many items of this type around anyway. + return false; } if( check_components || is_comestible() || is_craft() ) { //Only check if at least one item isn't using the default recipe or is comestible @@ -2560,7 +2561,7 @@ void item::final_info( std::vector &info, const iteminfo_query *parts, "It is %d percent complete." ); const int percent_progress = item_counter / 100000; info.push_back( iteminfo( "DESCRIPTION", string_format( desc, - making->result_name(), + craft_data_->making->result_name(), percent_progress ) ) ); } else { info.push_back( iteminfo( "DESCRIPTION", type->description.translated() ) ); @@ -3799,7 +3800,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } else if( is_armor() && has_clothing_mod() ) { maintext = label( quantity ) + "+1"; } else if( is_craft() ) { - maintext = string_format( _( "in progress %s" ), making->result_name() ); + maintext = string_format( _( "in progress %s" ), craft_data_->making->result_name() ); if( charges > 1 ) { maintext += string_format( " (%d)", charges ); } @@ -5713,7 +5714,7 @@ bool item::is_brewable() const bool item::is_food_container() const { return ( !contents.empty() && contents.front().is_food() ) || ( is_craft() && - making->create_result().is_food_container() ); + craft_data_->making->create_result().is_food_container() ); } bool item::has_temperature() const @@ -6034,7 +6035,7 @@ bool item::is_salvageable() const bool item::is_craft() const { - return making != nullptr; + return craft_data_ != nullptr; } bool item::is_funnel_container( units::volume &bigger_than ) const @@ -9451,37 +9452,43 @@ void item::set_favorite( const bool favorite ) const recipe &item::get_making() const { - if( !making ) { + if( !craft_data_ ) { debugmsg( "'%s' is not a craft or has a null recipe", tname() ); - return recipe().ident().obj(); + static const recipe dummy{}; + return dummy; } - return *making; + assert( craft_data_->making ); + return *craft_data_->making; } void item::set_tools_to_continue( bool value ) { - tools_to_continue = value; + assert( craft_data_ ); + craft_data_->tools_to_continue = value; } bool item::has_tools_to_continue() const { - return tools_to_continue; + assert( craft_data_ ); + return craft_data_->tools_to_continue; } void item::set_cached_tool_selections( const std::vector> &selections ) { - cached_tool_selections = selections; + assert( craft_data_ ); + craft_data_->cached_tool_selections = selections; } const std::vector> &item::get_cached_tool_selections() const { - return cached_tool_selections; + assert( craft_data_ ); + return craft_data_->cached_tool_selections; } const cata::value_ptr &item::get_comestible() const { if( is_craft() ) { - return find_type( making->result() )->comestible; + return find_type( craft_data_->making->result() )->comestible; } else { return type->comestible; } diff --git a/src/item.h b/src/item.h index b1a5017b510bc..d54d521738b31 100644 --- a/src/item.h +++ b/src/item.h @@ -15,6 +15,7 @@ #include #include "calendar.h" +#include "value_ptr.h" #include "cata_utility.h" #include "craft_command.h" #include "debug.h" @@ -42,12 +43,11 @@ namespace cata { template class optional; -template -class value_ptr; } // namespace cata class nc_color; class JsonIn; class JsonOut; +class JsonObject; class iteminfo_query; template class ret_val; @@ -2113,13 +2113,26 @@ class item : public visitable std::string corpse_name; // Name of the late lamented std::set techniques; // item specific techniques - // Only for in-progress crafts - const recipe *making = nullptr; - int next_failure_point = -1; - std::vector comps_used; - // If the crafter has insufficient tools to continue to the next 5% progress step - bool tools_to_continue = false; - std::vector> cached_tool_selections; + /** + * Data for items that represent in-progress crafts. + */ + class craft_data + { + public: + const recipe *making = nullptr; + int next_failure_point = -1; + std::vector comps_used; + // If the crafter has insufficient tools to continue to the next 5% progress step + bool tools_to_continue = false; + std::vector> cached_tool_selections; + + void serialize( JsonOut &jsout ) const; + void deserialize( JsonIn &jsin ); + void deserialize( const JsonObject &obj ); + }; + + cata::value_ptr craft_data_; + // any relic data specific to this item cata::optional relic_data; public: diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 3545b43368581..2003b37d29c76 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2034,6 +2034,47 @@ void units::energy::deserialize( JsonIn &jsin ) static void migrate_toolmod( item &it ); +void item::craft_data::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + jsout.member( "making", making->ident().str() ); + jsout.member( "comps_used", comps_used ); + jsout.member( "next_failure_point", next_failure_point ); + jsout.member( "tools_to_continue", tools_to_continue ); + jsout.member( "cached_tool_selections", cached_tool_selections ); + jsout.end_object(); +} + +void item::craft_data::deserialize( JsonIn &jsin ) +{ + deserialize( jsin.get_object() ); +} + +void item::craft_data::deserialize( const JsonObject &obj ) +{ + making = &recipe_id( obj.get_string( "making" ) ).obj(); + obj.read( "comps_used", comps_used ); + next_failure_point = obj.get_int( "next_failure_point", -1 ); + tools_to_continue = obj.get_bool( "tools_to_continue", false ); + obj.read( "cached_tool_selections", cached_tool_selections ); +} + +// Template parameter because item::craft_data is private and I don't want to make it public. +template +static void load_legacy_craft_data( io::JsonObjectInputArchive &archive, T &value ) +{ + if( archive.has_member( "making" ) ) { + value = cata::make_value(); + value->deserialize( archive ); + } +} + +// Dummy function as we never load anything from an output archive. +template +static void load_legacy_craft_data( io::JsonObjectOutputArchive &, T & ) +{ +} + template void item::io( Archive &archive ) { @@ -2055,10 +2096,6 @@ void item::io( Archive &archive ) corpse = &mtype_id( id ).obj(); } }; - const auto load_making = [this]( const std::string & id ) { - making = &recipe_id( id ).obj(); - }; - archive.template io( "typeid", type, load_type, []( const itype & i ) { return i.get_id(); }, io::required_tag() ); @@ -2109,17 +2146,10 @@ void item::io( Archive &archive ) []( const mtype & i ) { return i.id.str(); } ); - archive.template io( "making", making, load_making, - []( const recipe & i ) { - return i.ident().str(); - } ); + archive.io( "craft_data", craft_data_, decltype( craft_data_ )() ); archive.io( "light", light.luminance, nolight.luminance ); archive.io( "light_width", light.width, nolight.width ); archive.io( "light_dir", light.direction, nolight.direction ); - archive.io( "comps_used", comps_used, io::empty_default_tag() ); - archive.io( "next_failure_point", next_failure_point, -1 ); - archive.io( "tools_to_continue", tools_to_continue, false ); - archive.io( "cached_tool_selections", cached_tool_selections, io::empty_default_tag() ); archive.io( "relic_data", relic_data ); @@ -2130,6 +2160,8 @@ void item::io( Archive &archive ) } /* Loading has finished, following code is to ensure consistency and fixes bugs in saves. */ + load_legacy_craft_data( archive, craft_data_ ); + double float_damage = 0; if( archive.read( "damage", float_damage ) ) { damage_ = std::min( std::max( min_damage(), diff --git a/src/value_ptr.h b/src/value_ptr.h index c9b8c98a8912e..a89c894a915e1 100644 --- a/src/value_ptr.h +++ b/src/value_ptr.h @@ -4,6 +4,9 @@ #include +class JsonIn; +class JsonOut; + namespace cata { @@ -24,6 +27,24 @@ class value_ptr : public std::unique_ptr std::unique_ptr::operator=( std::move( other ) ); return *this; } + + template + void serialize( Stream &jsout ) const { + if( this->get() ) { + this->get()->serialize( jsout ); + } else { + jsout.write_null(); + } + } + template + void deserialize( Stream &jsin ) { + if( jsin.test_null() ) { + this->reset(); + } else { + this->reset( new T() ); + this->get()->deserialize( jsin ); + } + } }; template