polserver/pol-core/pol/proplist.cpp
turleypol 08bd3c7ce5
Savefile rewrite (#601)
* rewrite of saving, replaced operator way with a generic add method, thus
not everywhere the formating of the savefiles is hardcoded.
since its the last spot which uses the old formating lib, remobed the
lib

* removed cmake of old format lib

* testscripts for load/save of UObject,Item,Armor,Weapon

* fixed: member faster_cast_recovery and faster_cast_recovery_mod getter returned
a wrong value

* wrong/missing docs

* test containers, lockable, map, spellbooks
fixed save bug in map and spellbook
removed ancient savestrategy experiments

* added corpse test
splitted testfiles

* fixed loading (ancient bug) and saving (PR bug) of Reportables
added tests for houses, boats, accounts and chars

* fixed include

* fix shutdown leak

* added npc tests
upload complete coretest folder also on failure

* do not write and load RegisteredHouse for NPCs two times
readded extra newline between accounts

* tests for guild, party, realm
added resource cfgs, but a test is not really possible since nothing of
interest is stored

* added datafile, storage test

* activated resource loading, 25year old bug, but since the stored values
are senseless it makes no real difference.
added a few wtf comments
catch all exceptions during formatting of log lines

* replaced .write with explixit .begin, .end and .comment.
for leftovers.txt the StreamSaver was also used, since the format is
different use directly ofstream

* added corechanges
2024-01-24 18:49:06 +01:00

601 lines
16 KiB
C++

/** @file
*
* @par History
* - 2005/05/25 Shinigami: added PropertyList::printProperties( ConfigElem& elem )
*/
#include "proplist.h"
#include <stddef.h>
#include "../bscript/berror.h"
#include "../bscript/bobject.h"
#include "../bscript/executor.h"
#include "../bscript/impstr.h"
#include "../bscript/objmethods.h"
#include "../clib/cfgelem.h"
#include "../clib/logfacility.h"
#include "../clib/streamsaver.h"
#include "../clib/strutil.h"
#include "../plib/systemstate.h"
#include "baseobject.h"
#include "polcfg.h"
#define pf_endl '\n'
namespace Pol
{
namespace Core
{
CPropProfiler::HitsCounter::HitsCounter() : hits( std::array<u64, 3>{ { 0, 0, 0 } } ) {}
u64& CPropProfiler::HitsCounter::operator[]( size_t idx )
{
return hits[idx];
}
const u64& CPropProfiler::HitsCounter::operator[]( size_t idx ) const
{
return hits[idx];
}
size_t CPropProfiler::HitsCounter::sizeEstimate() const
{
return sizeof( void* ) + sizeof( u64 ) * hits.size();
}
/** Given an UOBJ_CLASS, returns the corresponding Type for profiling */
CPropProfiler::Type CPropProfiler::class_to_type( UOBJ_CLASS oclass )
{
switch ( oclass )
{
case UOBJ_CLASS::CLASS_ITEM:
case UOBJ_CLASS::CLASS_ARMOR:
case UOBJ_CLASS::CLASS_CONTAINER:
case UOBJ_CLASS::CLASS_WEAPON:
return CPropProfiler::Type::ITEM;
case UOBJ_CLASS::CLASS_CHARACTER:
case UOBJ_CLASS::CLASS_NPC:
return CPropProfiler::Type::MOBILE;
case UOBJ_CLASS::CLASS_MULTI:
return CPropProfiler::Type::MULTI;
case UOBJ_CLASS::INVALID:
return CPropProfiler::Type::UNKNOWN;
}
/// Must compute all cases, relying on GCC's -wSwitch option to check it
/// but placing a safe fallback anyway.
return CPropProfiler::Type::UNKNOWN;
}
CPropProfiler& CPropProfiler::instance()
{
static CPropProfiler instance;
return instance;
}
CPropProfiler::CPropProfiler() : _proplists( new PropLists() ), _hits( new Hits() ) {}
/**
* Returns proplist type, internal usage
*/
CPropProfiler::Type CPropProfiler::getProplistType( const PropertyList* proplist ) const
{
PropLists::iterator el;
{
Clib::SpinLockGuard lock( _proplistsLock );
el = _proplists->find( proplist );
}
if ( el == _proplists->end() )
{
/// Unknown should happen only when the profiler has been disabled and
/// then re-enabled (including when it was disabled at startup and enabled
/// later). In any other case, it's a bug.
return Type::UNKNOWN;
}
return el->second;
}
/**
* Returns wether a given type should be ignored, intenal usage
*/
bool CPropProfiler::isIgnored( Type type ) const
{
if ( type == Type::DATAFILEELEMENT || type == Type::REGION )
return true;
return false;
}
/**
* Register a cprop read
*
* @param proplist Pointer to the registered list where this cprop resides
* @param name Name of the cprop
*/
void CPropProfiler::cpropRead( const PropertyList* proplist, const std::string& name )
{
cpropAction( proplist, name, HitsCounter::READ );
}
/**
* Register a cprop write
*
* @param proplist Pointer to the registered list where this cprop resides
* @param name Name of the cprop
*/
void CPropProfiler::cpropWrite( const PropertyList* proplist, const std::string& name )
{
cpropAction( proplist, name, HitsCounter::WRITE );
}
/**
* Register a cprop erase
*
* @param proplist Pointer to the registered list where this cprop resides
* @param name Name of the cprop
* @throws std::runtime_error When proplist is not registered
*/
void CPropProfiler::cpropErase( const PropertyList* proplist, const std::string& name )
{
cpropAction( proplist, name, HitsCounter::ERASE );
}
/**
* Registers a property list address
*
* @param proplist Pointer to the list to be registered
* @param type The type of the list to be registered
*/
void CPropProfiler::registerProplist( const PropertyList* proplist, const CPropProfiler::Type type )
{
Clib::SpinLockGuard lock( _proplistsLock );
_proplists->insert( std::make_pair( proplist, type ) );
}
/**
* Registers a property list with same type of an already registered one
*
* @param proplist Pointer to the list to be registered
* @param copiedFrom Pointer to the already registered list to copy the type from
*/
void CPropProfiler::registerProplist( const PropertyList* proplist, const PropertyList* copiedFrom )
{
registerProplist( proplist, getProplistType( copiedFrom ) );
}
/**
* Unregisters a property list address
*/
void CPropProfiler::unregisterProplist( const PropertyList* proplist )
{
Clib::SpinLockGuard lock( _proplistsLock );
_proplists->erase( proplist );
}
/**
* Register a cprop action
*
* @param proplist Pointer to the registered list where this cprop resides
* @param name Name of the cprop
* @param key Index of the array key to update
*/
void CPropProfiler::cpropAction( const PropertyList* proplist, const std::string& name,
const size_t key )
{
Type type = getProplistType( proplist );
if ( isIgnored( type ) )
return;
{
Clib::SpinLockGuard lock( _hitsLock );
u64* cur = &( *_hits )[type][name][key];
if ( *cur < std::numeric_limits<u64>::max() )
( *cur )++;
}
}
/**
* Discards all the gathered data
*/
void CPropProfiler::clear()
{
Clib::SpinLockGuard plock( _proplistsLock );
Clib::SpinLockGuard hlock( _hitsLock );
_proplists->clear();
_hits->clear();
}
/**
* Dumps current status into a given stream
*
* @param os The output stream to write into
*/
void CPropProfiler::dumpProfile( std::ostream& os ) const
{
// First generate the data
// map<categoryname, map<typename, vector<lines> >>
std::map<std::string, std::map<std::string, std::vector<std::string>>> outData;
{
Clib::SpinLockGuard lock( _hitsLock );
for ( auto tIter = _hits->begin(); tIter != _hits->end(); ++tIter )
{
Type t = tIter->first;
std::string typeName;
switch ( t )
{
case Type::ACCOUNT:
typeName = "Account";
break;
case Type::GUILD:
typeName = "Guild";
break;
case Type::GLOBAL:
typeName = "Global";
break;
case Type::ITEM:
typeName = "Item";
break;
case Type::MOBILE:
typeName = "Mobile";
break;
case Type::MULTI:
typeName = "Multi";
break;
case Type::PARTY:
typeName = "Party";
break;
case Type::UNKNOWN:
typeName = "UNKNOWN";
break;
default:
typeName = "ERROR " + std::to_string( static_cast<unsigned int>( t ) );
break;
}
for ( auto pIter = tIter->second.begin(); pIter != tIter->second.end(); ++pIter )
{
std::ostringstream line;
line << pIter->first << " ";
line << pIter->second[HitsCounter::READ] << "/";
line << pIter->second[HitsCounter::WRITE] << "/";
line << pIter->second[HitsCounter::ERASE] << std::endl;
if ( !pIter->second[HitsCounter::READ] )
outData["WRITTEN BUT NEVER READ"][typeName].push_back( line.str() );
else if ( !pIter->second[HitsCounter::WRITE] )
outData["READ BUT NEVER WRITTEN"][typeName].push_back( line.str() );
else
outData["ALL THE REST"][typeName].push_back( line.str() );
}
}
}
// Then output it
for ( auto it1 = outData.rbegin(); it1 != outData.rend(); ++it1 )
{
// 1st level header
os << std::string( 15, '-' ) << " " << it1->first << " " << std::string( 15, '-' ) << std::endl;
for ( auto it2 = it1->second.begin(); it2 != it1->second.end(); ++it2 )
{
// 2nd level header
os << it2->first << " CProps summary (read/write/erase):" << std::endl;
std::sort( it2->second.begin(), it2->second.end() );
for ( auto it3 = it2->second.begin(); it3 != it2->second.end(); ++it3 )
{
os << "- " << *it3;
}
}
os << std::endl;
}
}
/**
* Returns the estimated in-memory size of the profiler
*/
size_t CPropProfiler::estimateSize() const
{
/// Size of base empty containers
size_t ret = sizeof( Clib::SpinLock ) + sizeof( _proplistsLock ) + sizeof( _hits ) +
sizeof( _proplists ) + sizeof( void* ) * 2;
/// + size of proplists
{
Clib::SpinLockGuard lock( _proplistsLock );
ret += ( sizeof( PropertyList* ) + sizeof( Type ) ) * _proplists->size();
}
/// + size of hits
{
Clib::SpinLockGuard lock( _hitsLock );
for ( auto itr1 = _hits->begin(); itr1 != _hits->end(); ++itr1 )
{
ret += sizeof( Type ) + sizeof( HitsEntries );
for ( auto itr2 = itr1->second.begin(); itr2 != itr1->second.end(); ++itr2 )
{
ret += itr2->first.size() + itr2->second.sizeEstimate();
}
}
}
return ret;
}
/**
* Initialize and register this property list based on a given type
* register only if the profile_cprops flag is set
*/
PropertyList::PropertyList( CPropProfiler::Type type ) : properties()
{
if ( Plib::systemstate.config.profile_cprops )
CPropProfiler::instance().registerProplist( this, type );
}
/**
* Initialize and register this property list based on a given type,
* always register if force flag is is true
*/
PropertyList::PropertyList( CPropProfiler::Type type, bool force ) : properties()
{
if ( force || Plib::systemstate.config.profile_cprops )
CPropProfiler::instance().registerProplist( this, type );
}
/**
* Initialize by copying content and type from a given one
*/
PropertyList::PropertyList( const PropertyList& props ) : properties()
{
copyprops( props );
if ( Plib::systemstate.config.profile_cprops )
CPropProfiler::instance().registerProplist( this, &props );
}
size_t PropertyList::estimatedSize() const
{
size_t size = sizeof( PropertyList );
size += properties.size() *
( sizeof( boost_utils::cprop_name_flystring ) +
sizeof( boost_utils::cprop_value_flystring ) + ( sizeof( void* ) * 3 + 1 ) / 2 );
return size;
}
bool PropertyList::getprop( const std::string& propname, std::string& propval ) const
{
if ( Plib::systemstate.config.profile_cprops )
CPropProfiler::instance().cpropRead( this, propname );
Properties::const_iterator itr = properties.find( boost_utils::cprop_name_flystring( propname ) );
if ( itr == properties.end() )
{
return false;
}
else
{
propval = ( *itr ).second;
return true;
}
}
void PropertyList::setprop( const std::string& propname, const std::string& propvalue )
{
if ( Plib::systemstate.config.profile_cprops )
CPropProfiler::instance().cpropWrite( this, propname );
properties[boost_utils::cprop_name_flystring( propname )] = propvalue;
}
void PropertyList::eraseprop( const std::string& propname )
{
if ( Plib::systemstate.config.profile_cprops )
CPropProfiler::instance().cpropErase( this, propname );
properties.erase( boost_utils::cprop_name_flystring( propname ) );
}
void PropertyList::copyprops( const PropertyList& from )
{
// dave 4/25/3 map insert won't overwrite with new values, so remove those first and then
// reinsert.
if ( !properties.empty() )
{
for ( const auto& prop : from.properties )
properties.erase( prop.first );
}
properties.insert( from.properties.begin(), from.properties.end() );
}
void PropertyList::clear()
{
properties.clear();
}
void PropertyList::getpropnames( std::vector<std::string>& propnames ) const
{
for ( const auto& prop : properties )
{
propnames.push_back( prop.first );
}
}
void PropertyList::printProperties( Clib::StreamWriter& sw ) const
{
for ( const auto& prop : properties )
{
const std::string& first = prop.first;
if ( first[0] != '#' )
{
sw.add( "CProp", fmt::format( "{} {}", first, prop.second.get() ) );
}
}
}
void PropertyList::printProperties( Clib::ConfigElem& elem ) const
{
for ( const auto& prop : properties )
{
const std::string& first = prop.first;
if ( first[0] != '#' )
{
elem.add_prop( "CProp", ( first + "\t" + prop.second.get() ) );
}
}
}
void PropertyList::printPropertiesAsStrings( Clib::StreamWriter& sw ) const
{
for ( const auto& prop : properties )
{
const std::string& first = prop.first;
if ( first[0] != '#' )
{
sw.add( first, prop.second.get() );
}
}
}
void PropertyList::readProperties( Clib::ConfigElem& elem )
{
std::string tempstr;
while ( elem.remove_prop( "StrProp", &tempstr ) )
{
std::string propname;
std::string propvalue;
Clib::splitnamevalue( tempstr, propname, propvalue );
setprop( propname, "s" + propvalue );
}
while ( elem.remove_prop( "CProp", &tempstr ) )
{
std::string propname;
std::string propvalue;
Clib::splitnamevalue( tempstr, propname, propvalue );
setprop( propname, propvalue );
}
}
void PropertyList::readRemainingPropertiesAsStrings( Clib::ConfigElem& elem )
{
std::string propname, propvalue;
while ( elem.remove_first_prop( &propname, &propvalue ) )
{
setprop( propname, propvalue );
}
}
bool PropertyList::operator==( const PropertyList& plist ) const
{
return ( this->properties == plist.properties );
}
PropertyList& PropertyList::operator-(
const std::set<std::string>& CPropNames ) // dave added 1/26/3
{
for ( const auto& name : CPropNames )
{
eraseprop( name );
}
return *this;
}
void PropertyList::operator-=( const std::set<std::string>& CPropNames ) // dave added 1/26/3
{
for ( const auto& name : CPropNames )
{
eraseprop( name );
}
}
Bscript::BObjectImp* CallPropertyListMethod_id( PropertyList& proplist, const int id,
Bscript::Executor& ex, bool& changed )
{
using namespace Bscript;
switch ( id )
{
case MTH_GETPROP:
{
if ( !ex.hasParams( 1 ) )
return new BError( "Not enough parameters" );
const String* propname_str;
if ( !ex.getStringParam( 0, propname_str ) )
return new BError( "Invalid parameter type" );
std::string val;
if ( !proplist.getprop( propname_str->value(), val ) )
return new BError( "Property not found" );
return Bscript::BObjectImp::unpack( val.c_str() );
}
case MTH_SETPROP:
{
if ( !ex.hasParams( 2 ) )
return new BError( "Not enough parameters" );
const String* propname_str;
if ( !ex.getStringParam( 0, propname_str ) )
return new BError( "Invalid parameter type" );
Bscript::BObjectImp* propval = ex.getParamImp( 1 );
if ( propval->isa( Bscript::BObjectImp::OTError ) )
{
POLLOGLN( "wtf, setprop w/ an error '{}' PC:{}", ex.scriptname().c_str(), ex.PC );
}
std::string propname = propname_str->value();
proplist.setprop( propname, propval->pack() );
if ( propname[0] != '#' )
changed = true;
return new BLong( 1 );
}
case MTH_ERASEPROP:
{
if ( !ex.hasParams( 1 ) )
return new BError( "Not enough parameters" );
const String* propname_str;
if ( !ex.getStringParam( 0, propname_str ) )
return new BError( "Invalid parameter type" );
std::string propname = propname_str->value();
proplist.eraseprop( propname );
if ( propname[0] != '#' )
changed = true;
return new BLong( 1 );
}
case MTH_PROPNAMES:
{
std::vector<std::string> propnames;
proplist.getpropnames( propnames );
std::unique_ptr<ObjArray> arr( new ObjArray );
for ( const auto& name : propnames )
{
arr->addElement( new String( name ) );
}
return arr.release();
}
default:
return nullptr;
}
}
Bscript::BObjectImp* CallPropertyListMethod( PropertyList& proplist, const char* methodname,
Bscript::Executor& ex, bool& changed )
{
Bscript::ObjMethod* objmethod = Bscript::getKnownObjMethod( methodname );
if ( objmethod != nullptr )
return CallPropertyListMethod_id( proplist, objmethod->id, ex, changed );
else
return nullptr;
}
} // namespace Core
} // namespace Pol