/** @file * * @par History * - 2005/11/26 Shinigami: changed "strcmp" into "stricmp" to suppress Script Errors */ #include "storage.h" #include #include #include #include "../bscript/berror.h" #include "../bscript/bobject.h" #include "../bscript/contiter.h" #include "../bscript/impstr.h" #include "../clib/cfgelem.h" #include "../clib/cfgfile.h" #include "../clib/clib.h" #include "../clib/logfacility.h" #include "../clib/rawtypes.h" #include "../clib/stlutil.h" #include "../clib/streamsaver.h" #include "../plib/poltype.h" #include "../plib/systemstate.h" #include "containr.h" #include "fnsearch.h" #include "globals/object_storage.h" #include "globals/uvars.h" #include "item/item.h" #include "loaddata.h" #include "mkscrobj.h" #include "ufunc.h" namespace Pol::Core { using namespace Bscript; StorageArea::StorageArea( std::string name ) : _name( name ) {} StorageArea::~StorageArea() { while ( !_items.empty() ) { Cont::iterator itr = _items.begin(); Items::Item* item = ( *itr ).second; item->destroy(); _items.erase( itr ); } } size_t StorageArea::estimateSize() const { size_t size = _name.capacity() + Clib::memsize( _items ); return size; } Items::Item* StorageArea::find_root_item( const std::string& name ) { // LINEAR_SEARCH Cont::iterator itr = _items.find( name ); if ( itr != _items.end() ) { return ( *itr ).second; } return nullptr; } bool StorageArea::delete_root_item( const std::string& name ) { Cont::iterator itr = _items.find( name ); if ( itr != _items.end() ) { Items::Item* item = ( *itr ).second; item->destroy(); _items.erase( itr ); return true; } return false; } void StorageArea::insert_root_item( Items::Item* item ) { item->inuse( true ); _items.insert( make_pair( item->name(), item ) ); } extern Items::Item* read_item( Clib::ConfigElem& elem ); // from UIMPORT.CPP void StorageArea::load_item( Clib::ConfigElem& elem ) { u32 container_serial = 0; // defaults to item at storage root, (void)elem.remove_prop( "CONTAINER", &container_serial ); // so the return value can be ignored Items::Item* item = read_item( elem ); // Austin added 8/10/2006, protect against further crash if item is null. Should throw instead? if ( item == nullptr ) { elem.warn_with_line( "Error reading item SERIAL or OBJTYPE." ); return; } if ( container_serial == 0 ) { insert_root_item( item ); } else { Items::Item* cont_item = Core::system_find_item( container_serial ); if ( cont_item ) { add_loaded_item( cont_item, item ); } else { defer_item_insertion( item, container_serial ); } } } StorageArea* Storage::find_area( const std::string& name ) { AreaCont::iterator itr = areas.find( name ); if ( itr == areas.end() ) return nullptr; return ( *itr ).second; } StorageArea* Storage::create_area( const std::string& name ) { AreaCont::iterator itr = areas.find( name ); if ( itr == areas.end() ) { StorageArea* area = new StorageArea( name ); areas[name] = area; return area; } return ( *itr ).second; } StorageArea* Storage::create_area( Clib::ConfigElem& elem ) { const char* rest = elem.rest(); if ( rest != nullptr && rest[0] ) { return create_area( rest ); } std::string name = elem.remove_string( "NAME" ); return create_area( name ); } void StorageArea::print( Clib::StreamWriter& sw ) const { for ( const auto& cont_item : _items ) { const Items::Item* item = cont_item.second; if ( item->saveonexit() ) item->printOn( sw ); } } void StorageArea::on_delete_realm( Realms::Realm* realm ) { for ( const auto& _item : _items ) { Items::Item* item = _item.second; if ( item ) { setrealmif( item, (void*)realm ); if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) ) { UContainer* cont = static_cast( item ); cont->for_each_item( setrealmif, (void*)realm ); } } } } void Storage::on_delete_realm( Realms::Realm* realm ) { for ( const auto& area : areas ) { area.second->on_delete_realm( realm ); } } void Storage::read( Clib::ConfigFile& cf ) { static int num_until_dot = 1000; unsigned int nobjects = 0; StorageArea* area = nullptr; Clib::ConfigElem elem; clock_t start = clock(); while ( cf.read( elem ) ) { if ( --num_until_dot == 0 ) { INFO_PRINT( "." ); num_until_dot = 1000; } if ( elem.type_is( "StorageArea" ) ) { area = create_area( elem ); } else if ( elem.type_is( "Item" ) ) { if ( area != nullptr ) { try { area->load_item( elem ); } catch ( std::exception& ) { if ( !Plib::systemstate.config.ignore_load_errors ) throw; } } else { ERROR_PRINTLN( "Storage: Got an ITEM element, but don't have a StorageArea to put it." ); throw std::runtime_error( "Data file integrity error" ); } } else { ERROR_PRINTLN( "Unexpected element type {} in storage file.", elem.type() ); throw std::runtime_error( "Data file integrity error" ); } ++nobjects; } clock_t end = clock(); int ms = static_cast( ( end - start ) * 1000.0 / CLOCKS_PER_SEC ); INFO_PRINTLN( " {} elements in {} ms.", nobjects, ms ); } void Storage::print( Clib::StreamWriter& sw ) const { for ( const auto& area : areas ) { sw.begin( "StorageArea" ); sw.add( "Name", area.first ); sw.end(); area.second->print( sw ); } } void Storage::clear() { while ( !areas.empty() ) { delete ( ( *areas.begin() ).second ); areas.erase( areas.begin() ); } } size_t Storage::estimateSize() const { size_t size = Clib::memsize( areas ); for ( const auto& area : areas ) { if ( area.second != nullptr ) size += area.second->estimateSize(); } return size; } class StorageAreaIterator final : public ContIterator { public: StorageAreaIterator( StorageArea* area, BObject* pIter ); BObject* step() override; private: BObject* m_pIterVal; std::string key; StorageArea* _area; }; StorageAreaIterator::StorageAreaIterator( StorageArea* area, BObject* pIter ) : ContIterator(), m_pIterVal( pIter ), key( "" ), _area( area ) { } BObject* StorageAreaIterator::step() { StorageArea::Cont::iterator itr = _area->_items.lower_bound( key ); if ( !key.empty() && itr != _area->_items.end() ) { ++itr; } if ( itr == _area->_items.end() ) return nullptr; key = ( *itr ).first; m_pIterVal->setimp( new String( key ) ); BObject* result = new BObject( make_itemref( ( *itr ).second ) ); return result; } ContIterator* StorageAreaImp::createIterator( BObject* pIterVal ) { return new StorageAreaIterator( _area, pIterVal ); } BObjectRef StorageAreaImp::get_member( const char* membername ) { if ( stricmp( membername, "count" ) == 0 ) { return BObjectRef( new BLong( static_cast( _area->_items.size() ) ) ); } if ( stricmp( membername, "totalcount" ) == 0 ) { unsigned int total = 0; for ( auto& _item : _area->_items ) { Items::Item* item = _item.second; total += item->item_count(); } return BObjectRef( new BLong( total ) ); } return BObjectRef( UninitObject::create() ); } class StorageAreasIterator final : public ContIterator { public: StorageAreasIterator( BObject* pIter ); BObject* step() override; private: BObject* m_pIterVal; std::string key; }; StorageAreasIterator::StorageAreasIterator( BObject* pIter ) : ContIterator(), m_pIterVal( pIter ), key( "" ) { } BObject* StorageAreasIterator::step() { Storage::AreaCont::iterator itr = gamestate.storage.areas.lower_bound( key ); if ( !key.empty() && itr != gamestate.storage.areas.end() ) { ++itr; } if ( itr == gamestate.storage.areas.end() ) return nullptr; key = ( *itr ).first; m_pIterVal->setimp( new String( key ) ); BObject* result = new BObject( new StorageAreaImp( ( *itr ).second ) ); return result; } class StorageAreasImp final : public BObjectImp { public: StorageAreasImp() : BObjectImp( BObjectImp::OTUnknown ) {} BObjectImp* copy() const override { return new StorageAreasImp(); } std::string getStringRep() const override { return ""; } size_t sizeEstimate() const override { return sizeof( *this ); } ContIterator* createIterator( BObject* pIterVal ) override { return new StorageAreasIterator( pIterVal ); } BObjectRef get_member( const char* membername ) override; BObjectRef OperSubscript( const BObject& obj ) override; }; BObjectImp* CreateStorageAreasImp() { return new StorageAreasImp(); } BObjectRef StorageAreasImp::get_member( const char* membername ) { if ( stricmp( membername, "count" ) == 0 ) { return BObjectRef( new BLong( static_cast( gamestate.storage.areas.size() ) ) ); } return BObjectRef( UninitObject::create() ); } BObjectRef StorageAreasImp::OperSubscript( const BObject& obj ) { if ( obj.isa( OTString ) ) { String& rtstr = (String&)obj.impref(); std::string key = rtstr.value(); Storage::AreaCont::iterator itr = gamestate.storage.areas.find( key ); if ( itr != gamestate.storage.areas.end() ) { return BObjectRef( new BObject( new StorageAreaImp( ( *itr ).second ) ) ); } return BObjectRef( new BObject( new BError( "Storage Area not found" ) ) ); } return BObjectRef( new BObject( new BError( "Invalid parameter type" ) ) ); } } // namespace Pol::Core