//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: CEconItem, a shared object for econ items // //============================================================================= #ifndef ECONITEM_H #define ECONITEM_H #ifdef _WIN32 #pragma once #endif #include "gcsdk/gcclientsdk.h" #include "base_gcmessages.pb.h" #include "econ_item_constants.h" #include "econ_item_interface.h" #include "econ_item_schema.h" #include // needed for typeid() #define ENABLE_TYPED_ATTRIBUTE_PARANOIA 1 #ifdef GC_DLL class CSchItem; class CEconSharedObjectCache; #endif namespace GCSDK { class CColumnSet; #ifdef GC_DLL class CWebAPIValues; #endif }; class CEconItem; class CSOEconItem; class CEconItemCustomData; class CEconSessionItemAudit; //----------------------------------------------------------------------------- // Stats tracking for the attributes attached to CEconItem instances. //----------------------------------------------------------------------------- struct schema_attribute_stat_bucket_t { const schema_attribute_stat_bucket_t *m_pNext; const char *m_pszDesc; uint64 m_unLiveInlineCount; uint64 m_unLifetimeInlineCount; uint64 m_unLiveHeapCount; uint64 m_unLifetimeHeapCount; void OnAllocateInlineInstance() { m_unLiveInlineCount++; m_unLifetimeInlineCount++; } void OnFreeInlineInstance() { Assert( m_unLiveInlineCount > 0 ); m_unLiveInlineCount--; } void OnAllocateHeapInstance() { m_unLiveHeapCount++; m_unLifetimeHeapCount++; } void OnFreeHeapInstance() { Assert( m_unLiveHeapCount ); m_unLiveHeapCount--; } }; class CSchemaAttributeStats { public: template < typename TAttribStatsStorageClass, typename TAttribInMemoryType > static void RegisterAttributeType() { TAttribStatsStorageClass::s_InstanceStats.m_pszDesc = typeid( TAttribInMemoryType ).name(); TAttribStatsStorageClass::s_InstanceStats.m_pNext = m_pHead; m_pHead = &TAttribStatsStorageClass::s_InstanceStats; } static const schema_attribute_stat_bucket_t *GetFirstStatBucket() { return m_pHead; } private: static const schema_attribute_stat_bucket_t *m_pHead; }; //----------------------------------------------------------------------------- // Base class interface for attributes of a certain in-memory type. //----------------------------------------------------------------------------- unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue(); template < typename T > unsigned int GetAttributeTypeUniqueIdentifier() { static unsigned int s_unUniqueCounter = Internal_GetAttributeTypeUniqueIdentifierNextValue(); return s_unUniqueCounter; } //----------------------------------------------------------------------------- // Base class interface for attributes of a certain in-memory type. //----------------------------------------------------------------------------- template < typename TAttribInMemoryType > class ISchemaAttributeTypeBase : public ISchemaAttributeType { friend class CSchemaAttributeStats; public: ISchemaAttributeTypeBase() { CSchemaAttributeStats::RegisterAttributeType< ISchemaAttributeTypeBase, TAttribInMemoryType >(); // The implementation of the attributes-in-memory system is such that it may or may not behave according to // expectations. Rather than have to stare at all the details to answer questions about where memory is allocated // or managed, or when it will be freed, for all our current use cases it makes more sense to just disable raw // pointer types from being an attribute-in-memory type and instead steer people towards this message explaining // why. COMPILE_TIME_ASSERT( !IsPointerType::kValue ); } #ifdef GC_DLL // By default, without a specific type we don't support any sort of custom value generation, so all we can do // to load an attribute is to copy the value out from the generic format (union) and turn it into whatever our // type is, and then add that type to the item as an attribute. // // Unlike most of the functions in this class, this is not meant to be a catch-all default implementation but // is instead a base implementation. Subclasses are intended to override to add or change functionality. virtual void LoadOrGenerateEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const OVERRIDE { Assert( pTargetItem ); Assert( pAttrDef ); AssertMsg( !staticAttrib.m_pKVCustomData, "Default implementation of LoadOrGenerateEconAttributeValue() doesn't support custom value generation!" ); AssertMsg( pGameAccount || !staticAttrib.m_pKVCustomData, "Cannot run custom logic with no game account object! Passing in NULL for pGameAccount is only supported when we know we won't be running custom value generation code!" ); LoadEconAttributeValue( pTargetItem, pAttrDef, staticAttrib.m_value ); } // By default, we dont generate any custom value virtual void GenerateEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const OVERRIDE { Assert( pAttrDef ); Assert( pGameAccount ); Assert( out_pValue ); } #endif // GC_DLL virtual void LoadEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value ) const OVERRIDE; // Returns a unique identifier per run based on the type of . virtual unsigned int GetTypeUniqueIdentifier() const OVERRIDE { return GetAttributeTypeUniqueIdentifier(); } // Takes the value specified in [typedValue] and stores it in the most appropriate way // somewhere attached to [out_pValue]. This may hit the heap. The storage itself is // intended to be opaque but can be reversed by calling GetTypedValueContentsFromEconAttributeValue(). void ConvertTypedValueToEconAttributeValue( const TAttribInMemoryType& typedValue, attribute_data_union_t *out_pValue ) const { // If our type is smaller than an int, we don't know how to copy the memory into our flat structure. We could write // this code but we have no use case for it now so this is set up to fail so if someone does come up with a use case // they know where to fix. COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) ); // Do we fit in the bottom 32-bits? if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) ) { *reinterpret_cast( &out_pValue->asUint32 ) = typedValue; } // What about in the full 64-bits (if we're running a 64-bit build)? else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) ) { *reinterpret_cast( &out_pValue->asBlobPointer ) = typedValue; } // We're too big for our flat structure. We need to allocate space somewhere outside our attribute instance and point // to that. else { Assert( out_pValue->asBlobPointer ); *reinterpret_cast( out_pValue->asBlobPointer ) = typedValue; } } // Guaranteed to return a valid reference (or assert/crash if calling code is behaving inappropriately and calling // this before an attribute value is allocated/set). const TAttribInMemoryType& GetTypedValueContentsFromEconAttributeValue( const attribute_data_union_t& value ) const { COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) ); // Do we fit in the bottom 32-bits? if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) ) return *reinterpret_cast( &value.asUint32 ); // What about in the full 64-bits (if we're running a 64-bit build)? if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) ) return *reinterpret_cast( &value.asBlobPointer ); // We don't expect to get to a "read value" call without having written a value, which would // have allocated this memory. Assert( value.asBlobPointer ); return *reinterpret_cast( value.asBlobPointer ); } void ConvertEconAttributeValueToTypedValue( const attribute_data_union_t& value, TAttribInMemoryType *out_pTypedValue ) const { Assert( out_pTypedValue ); *out_pTypedValue = GetTypedValueContentsFromEconAttributeValue( value ); } void InitializeNewEconAttributeValue( attribute_data_union_t *out_pValue ) const OVERRIDE { if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) ) { new( &out_pValue->asUint32 ) TAttribInMemoryType; s_InstanceStats.OnAllocateInlineInstance(); } else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) ) { new( &out_pValue->asBlobPointer ) TAttribInMemoryType; s_InstanceStats.OnAllocateInlineInstance(); } else { out_pValue->asBlobPointer = reinterpret_cast( new TAttribInMemoryType ); s_InstanceStats.OnAllocateHeapInstance(); } } virtual void UnloadEconAttributeValue( attribute_data_union_t *out_pValue ) const OVERRIDE { COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) ); // For smaller types, anything that fits inside the bits of a void pointer, we store the contents // inline and only have to worry about calling the correct destructor. We check against the small-/ // size/medium-size values separately to not worry about which bits we're storing the uint32 in. if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) ) { (reinterpret_cast( &out_pValue->asUint32 ))->~TAttribInMemoryType(); s_InstanceStats.OnFreeInlineInstance(); } else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) ) { (reinterpret_cast( &out_pValue->asBlobPointer ))->~TAttribInMemoryType(); s_InstanceStats.OnFreeInlineInstance(); } // For larger types, we have the memory stored on the heap somewhere. We don't have to manually // destruct, but we do have to manually free. else { Assert( out_pValue->asBlobPointer ); delete reinterpret_cast( out_pValue->asBlobPointer ); s_InstanceStats.OnFreeHeapInstance(); } } virtual bool OnIterateAttributeValue( IEconItemAttributeIterator *pIterator, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE { Assert( pIterator ); Assert( pAttrDef ); // Call the appropriate virtual function on our iterator based on whatever type we represent. return pIterator->OnIterateAttributeValue( pAttrDef, GetTypedValueContentsFromEconAttributeValue( value ) ); } virtual void LoadByteStreamToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const std::string& sBytes ) const OVERRIDE; virtual void ConvertEconAttributeValueToByteStream( const attribute_data_union_t& value, ::std::string *out_psBytes ) const; virtual void ConvertTypedValueToByteStream( const TAttribInMemoryType& typedValue, ::std::string *out_psBytes ) const = 0; virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TAttribInMemoryType *out_pTypedValue ) const = 0; private: static schema_attribute_stat_bucket_t s_InstanceStats; }; // This function exists only to back-convert code that relies on the old untyped // attribute system, doing things like shoving floating-point bits into a uint32 // value in the database. // // There is no reason to use this function moving forward! If you're writing new // code and calling this function seems like the only way to get the effect you // want, it probably just means that there is no attribute type for what you're // trying to do yet. template < typename T > uint32 WrapDeprecatedUntypedEconItemAttribute( T tValue ) { COMPILE_TIME_ASSERT( sizeof( T ) == sizeof( uint32 ) ); return *reinterpret_cast( &tValue ); } template < typename TAttribInMemoryType > schema_attribute_stat_bucket_t ISchemaAttributeTypeBase::s_InstanceStats; class CEconItem : public GCSDK::CSharedObject, public CMaterialOverrideContainer< IEconItemInterface > { #ifdef GC_DLL DECLARE_CLASS_MEMPOOL( CEconItem ); #endif public: typedef GCSDK::CSharedObject BaseClass; struct attribute_t { attrib_definition_index_t m_unDefinitionIndex; // stored as ints here for memory efficiency on the GC attribute_data_union_t m_value; private: void operator=( const attribute_t& rhs ); }; struct EquippedInstance_t { EquippedInstance_t() : m_unEquippedClass( 0 ), m_unEquippedSlot( INVALID_EQUIPPED_SLOT ) {} EquippedInstance_t( equipped_class_t unClass, equipped_slot_t unSlot ) : m_unEquippedClass( unClass ), m_unEquippedSlot( unSlot ) {} equipped_class_t m_unEquippedClass; equipped_slot_t m_unEquippedSlot; }; #ifdef GC_DLL class CAuditEntry { public: CAuditEntry( EItemAction eAction, uint32 unData ) : m_eAction( eAction ), m_unData( unData ) { } bool BAddAuditEntryToTransaction( CSQLAccess& sqlAccess, const CEconItem *pItem ) const; private: EItemAction m_eAction; uint32 m_unData; }; // Set only the top 16 bits for field ID types! These will be or'd into the index of // the field itself and then pulled apart later. enum { kUpdateFieldIDType_FieldID = 0x00000000, // this must stay as 0 for legacy code kUpdateFieldIDType_AttributeID = 0x00010000, }; #endif // GC_DLL const static int k_nTypeID = k_EEconTypeItem; virtual int GetTypeID() const { return k_nTypeID; } CEconItem(); CEconItem( const CEconItem& rhs ); virtual ~CEconItem(); CEconItem &operator=( const CEconItem& rhs ); //called to determine if this item is tradable or not. This will return the time after which it can be traded. If 0 it can be traded. This is //needed since the base implementation of this is protected RTime32 GetTradableAfterDateTime() const { return IEconItemInterface::GetTradableAfterDateTime(); } //called to set a tradable after date/time value onto this item (this avoids a lot of potential inefficiencies around this process) void SetTradableAfterDateTime( RTime32 rtTime ); // IEconItemInterface interface. const GameItemDefinition_t *GetItemDefinition() const; public: virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE; virtual itemid_t GetID() const { return GetItemID(); } // Accessors/Settors itemid_t GetItemID() const { return m_ulID; } void SetItemID( uint64 ulID ); itemid_t GetOriginalID() const; void SetOriginalID( uint64 ulOriginalID ); uint32 GetAccountID() const { return m_unAccountID; } void SetAccountID( uint32 unAccountID ) { m_unAccountID = unAccountID; } uint32 GetDefinitionIndex() const { return m_unDefIndex; } void SetDefinitionIndex( uint32 unDefinitionIndex ) { m_unDefIndex = unDefinitionIndex; } uint32 GetItemLevel() const { return m_unLevel; } void SetItemLevel( uint32 unItemLevel ) { m_unLevel = unItemLevel; } int32 GetQuality() const { return m_nQuality; } void SetQuality( int32 nQuality ) { m_nQuality = nQuality; } uint32 GetInventoryToken() const { return m_unInventory; } void SetInventoryToken( uint32 unToken ) { m_unInventory = unToken; } int GetQuantity() const; void SetQuantity( uint16 unQuantity ); uint8 GetFlags() const { return m_unFlags; } void SetFlags( uint8 unFlags ) { m_unFlags = unFlags; } void SetFlag( uint8 unFlag ) { m_unFlags |= unFlag; } void ClearFlag( uint8 unFlag ) { m_unFlags &= ~unFlag; } bool CheckFlags( uint8 unFlags ) const { return ( m_unFlags & unFlags ) != 0; } eEconItemOrigin GetOrigin() const { return (eEconItemOrigin)m_unOrigin; } void SetOrigin( eEconItemOrigin unOrigin ) { m_unOrigin = unOrigin; Assert( m_unOrigin == unOrigin ); } bool IsForeign() const { return m_unOrigin == kEconItemOrigin_Foreign; } style_index_t GetStyle() const; void SetStyle( uint8 unStyle ) { m_unStyle = unStyle; DirtyIconURL(); } const char *GetIconURLSmall() const; const char *GetIconURLLarge() const; const char *GetCustomName() const; void SetCustomName( const char *pName ); const char *GetCustomDesc() const; void SetCustomDesc( const char *pDesc ); bool IsEquipped() const; bool IsEquippedForClass( equipped_class_t unClass ) const; equipped_slot_t GetEquippedPositionForClass( equipped_class_t unClass ) const; void Equip( equipped_class_t unClass, equipped_slot_t unSlot ); void Unequip(); void UnequipFromClass( equipped_class_t unClass ); // This should really only used for the WebAPIs, debugging, etc. Data manipulation during gameplay should use // the above functions. int GetEquippedInstanceCount() const; const EquippedInstance_t &GetEquippedInstance( int iIdx ) const; virtual bool GetInUse() const; void SetInUse( bool bInUse ); bool IsTradable() const; bool IsMarketable() const; bool IsCommodity() const; void AdoptMoreRestrictedTradabilityFromItem( const CEconItem *pOther, uint32 nTradabilityFlagsToAccept = 0xFFFFFFFF ); void AdoptMoreRestrictedTradability( uint32 nTradabilityFlags, RTime32 nUntradableTime ); bool IsUsableInCrafting() const; #ifdef GC_DLL RTime32 GetAssetInfoExpirationCacheExpirationTime() const; #endif // GC_DLL // -------------------------------------------------------------------------------------------- // Typed attributes. These are methods for accessing and setting values of attributes with // some semblance of type information and type safety. // -------------------------------------------------------------------------------------------- // Assign the value of the attribute [pAttrDef] to [value]. Passing in a type for [value] that // doesn't match the storage type specified by the attribute definition will fail asserts a bunch // of asserts all the way down the stack and may or may not crash -- it would be nice to make this // fail asserts at compile time. // // This function has undefined results (besides asserting) if called to add a dynamic version of // an attrib that's already specified statically. template < typename T > void SetDynamicAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const T& value ) { Assert( pAttrDef ); const ISchemaAttributeTypeBase *pAttrType = GetTypedAttributeType( pAttrDef ); #ifdef GC_DLL // The GC is expected to always have internally-consistent information and so be able to access the // type information of any attribute if we started up successfully. Assert( pAttrType ); #else // Game clients and servers may be running code that doesn't have all of the types for the new attributes // for a GC that just propped. Because we're not authoritative over items here, about the best we can do // here is abort entirely. This means that the client may not display certain attributes at all, or even // have them in the attribute list in memory, but we don't understand those attributes anyway. if ( !pAttrType ) return; #endif // Fail right off the bat if we're trying to write a dynamic attribute value for an item that already // has this as a static value. AssertMsg4( !::FindAttribute( GetItemDefinition(), pAttrDef ), "Item id %llu (%s) attempting to set dynamic attribute value for '%s' (%d) when static attribute exists!", GetItemID(), GetItemDefinition()->GetDefinitionName(), pAttrDef->GetDefinitionName(), pAttrDef->GetDefinitionIndex() ); // Alright, we have a data type match so we can safely store data. Some types may need to initialize // their data to a current state if it's the first time we're writing to this value (as opposed to // updating an existing value). attribute_t *pEconAttrib = FindDynamicAttributeInternal( pAttrDef ); if ( !pEconAttrib ) { pEconAttrib = &(AddDynamicAttributeInternal()); pEconAttrib->m_unDefinitionIndex = pAttrDef->GetDefinitionIndex(); pAttrType->InitializeNewEconAttributeValue( &pEconAttrib->m_value ); } pAttrType->ConvertTypedValueToEconAttributeValue( value, &pEconAttrib->m_value ); #if ENABLE_TYPED_ATTRIBUTE_PARANOIA // Paranoia!: make sure that our read/write functions are mirrored correctly, and that if we attempt // to read back a value we get something identical to what we just wrote. We do this via converting // to strings and then comparing those because there may or not be equality comparisons for our type // T that make sense (ie., protobufs). { T readValue; DbgVerify( FindAttribute( pAttrDef, &readValue ) ); std::string sBytes, sReadBytes; pAttrType->ConvertTypedValueToByteStream( value, &sBytes ); pAttrType->ConvertTypedValueToByteStream( readValue, &sReadBytes ); AssertMsg1( sBytes == sReadBytes, "SetDynamicAttributeValue(): read/write mismatch for attribute '%s'.", pAttrDef->GetDefinitionName() ); } #endif // ENABLE_TYPED_ATTRIBUTE_PARANOIA } // Called to set a time stamp dynamic attribute on this item. But it will first check the current value assigned to this item, and will // only set it if this new time extends beyond the current one void SetDynamicMaxTimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, RTime32 rtTime ); // Remove an instance of an attribute from this item. This will also free any dynamic memory associated // with that instance if any was allocated. void RemoveDynamicAttribute( const CEconItemAttributeDefinition *pAttrDef ); // Copy all attributes and values in a type-safe way from [source] to ourself. Attributes that we have // that don't exist on [source] will maintain their current values. All other attributes will get their // values set to whatever [source] specifies. void CopyAttributesFrom( const CEconItem& source ); bool BHasDynamicAttributes() const { return GetDynamicAttributeCountInternal() > 0; } private: const char* FindIconURL( bool bLarge ) const; void Init(); template < typename T > static const ISchemaAttributeTypeBase *GetTypedAttributeType( const CEconItemAttributeDefinition *pAttrDef ) { // Make sure the type of data we're passing in matches the type of data we're claiming that we can // store in the attribute definition. const ISchemaAttributeType *pIAttr = pAttrDef->GetAttributeType(); Assert( pIAttr ); Assert( pIAttr->GetTypeUniqueIdentifier() == GetAttributeTypeUniqueIdentifier() ); #if ENABLE_TYPED_ATTRIBUTE_PARANOIA return dynamic_cast *>( pIAttr ); #else return static_cast *>( pIAttr ); #endif } public: void Compact(); #ifdef GC bool BDeserializeFromKV( KeyValues *pKVItem, CUtlVector *pVecErrors ); #endif // GC #ifdef GC_DLL void ExportToAPI( GCSDK::CWebAPIValues *pValues ) const; bool BImportFromAPI( GCSDK::CWebAPIValues *pValues ); #endif // GC_DLL // these are overridden to handle attributes #ifdef GC_DLL virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess ); virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields ); virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess ); void SerializeToSchemaItem( CSchItem &item ) const; void DeserializeFromSchemaItem( const CSchItem &item ); void SetInteriorItem( CEconItem* pInteriorItem ); #endif // GC_DLL virtual bool BParseFromMessage( const CUtlBuffer &buffer ) OVERRIDE; virtual bool BParseFromMessage( const std::string &buffer ) OVERRIDE; virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) OVERRIDE; #ifdef GC virtual bool BAddToMessage( CUtlBuffer & bufOutput ) const OVERRIDE; virtual bool BAddToMessage( std::string *pBuffer ) const OVERRIDE; // short cut to remove an extra copy virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const OVERRIDE; virtual bool BAddDestroyToMessage( std::string *pBuffer ) const OVERRIDE; bool BYieldingSerializeFromDatabase( itemid_t ulItemID ); #endif virtual bool BIsKeyLess( const CSharedObject & soRHS ) const ; virtual void Copy( const CSharedObject & soRHS ); virtual void Dump() const; virtual CUtlString GetDebugString() const OVERRIDE; void SerializeToProtoBufItem( CSOEconItem &msgItem ) const; void DeserializeFromProtoBufItem( const CSOEconItem &msgItem ); #ifdef GC_DLL CEconItem* YieldingGetInteriorItem(); const CEconItem* YieldingGetInteriorItem() const { return const_cast(this)->YieldingGetInteriorItem(); } void SetEquippedThisGameServerSession( bool bEquipped ) { m_bEquippedThisGameServerSession = bEquipped; } bool EquippedThisGameServerSession() const { return m_bEquippedThisGameServerSession; } #endif // Non-yielding -- will return current interior item if it exists and is already loaded // but will make no attempt to load. CEconItem* GetInteriorItem(); const CEconItem* GetInteriorItem() const { return const_cast(this)->GetInteriorItem(); } const CEconItemCustomData* GetCustomData() const { return m_pCustomData; } void OnTraded( uint32 unTradabilityDelaySeconds ); void OnReceivedFromMarket( bool bFromRollback ); protected: // Call this when the appearance of this item changes (ex. paintkit, style, festive). This will // cause the icon to be lazily re-evaluated (ie. so that changing the style will change the icon) void DirtyIconURL() { m_pszLargeIcon = NULL; m_pszSmallIcon = NULL; } // CSharedObject // adapted from CSchemaSharedObject void GetDirtyColumnSet( const CUtlVector< int > &fields, GCSDK::CColumnSet &cs ) const; void EnsureCustomDataExists(); bool BYieldingLoadInteriorItem(); void OnTransferredOwnership(); // Internal attribute interface. friend class CWebAPIStringExporterAttributeIterator; friend class CAttributeToStringIterator; attribute_t& AddDynamicAttributeInternal(); // add another chunk of data to our internal storage to store a new attribute -- initialization is the responsibility of the caller attribute_t *FindDynamicAttributeInternal( const CEconItemAttributeDefinition *pAttrDef ); // search for an instance of a dynamic attribute with this definition -- ignores static properties, etc. and will return NULL if not found int GetDynamicAttributeCountInternal() const; // how many attributes are there attached to this instance? attribute_t& GetMutableDynamicAttributeInternal( int iAttrIndexIntoArray ); // get a writable version of our attribute memory base chunk (added by AddDynamicAttributeInternal) for this index (same "array" as GetDynamicAttributeCountInternal) const attribute_t& GetDynamicAttributeInternal( int iAttrIndexIntoArray ) const // read-only version of our attribute memory base chunk for this index (same "array" as GetDynamicAttributeCountInternal) { return const_cast( this )->GetMutableDynamicAttributeInternal( iAttrIndexIntoArray ); } const EquippedInstance_t *FindEquippedInstanceForClass( equipped_class_t nClass ) const; void InternalVerifyEquipInstanceIntegrity() const; struct dirty_bits_t { // other uint8 m_bInUse : 1; uint8 m_bHasEquipSingleton : 1; uint8 m_bHasAttribSingleton: 1; }; mutable const char* m_pszSmallIcon; mutable const char* m_pszLargeIcon; public: // data that is most commonly changed uint64 m_ulID; // Item ID uint32 m_unAccountID; // Item Owner uint32 m_unInventory; // App managed int representing inventory placement item_definition_index_t m_unDefIndex; // Item definition index uint8 m_unLevel; // Item Level uint8 m_nQuality; // Item quality (rarity) uint8 m_unFlags; // Flags uint8 m_unOrigin; // Origin (eEconItemOrigin) style_index_t m_unStyle; // Style dirty_bits_t m_dirtyBits; // dirty bits // Fields that we often have zero or one of, but not often more EquippedInstance_t m_EquipInstanceSingleton; // Where the item is equipped. Valid only if m_bHasEquipSingleton and there is no custom data attribute_t m_CustomAttribSingleton; // Custom attribute. Valid only if m_bHasAttribSingleton and there is no custom data // optional data (custom name, additional attributes, etc.) CEconItemCustomData *m_pCustomData; #ifdef GC_DLL private: bool m_bEquippedThisGameServerSession; #endif // GC_DLL }; //----------------------------------------------------------------------------- // Purpose: Storage for data that is not commonly changed in CEconItem, primarily // as a memory savings mechanism. //----------------------------------------------------------------------------- class CEconItemCustomData { public: CEconItemCustomData() : m_pInteriorItem( NULL ) , m_ulOriginalID( INVALID_ITEM_ID ) , m_unQuantity( 1 ) , m_vecAttributes( /* grow size: */ 1, /* init size: */ 0 ) , m_vecEquipped( /* grow size: */ 1, /* init size: */ 0 ) {} ~CEconItemCustomData(); CUtlVector< CEconItem::attribute_t > m_vecAttributes; CEconItem* m_pInteriorItem; uint64 m_ulOriginalID; // Original Item ID uint16 m_unQuantity; // Consumable stack count (ammo, money, etc) CUtlVector m_vecEquipped; static void FreeAttributeMemory( CEconItem::attribute_t *pAttrib ); #ifdef GC_DLL DECLARE_CLASS_MEMPOOL( CEconItemCustomData ); #endif }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- template < typename TAttribInMemoryType > /*virtual*/ void ISchemaAttributeTypeBase::LoadByteStreamToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const std::string& sBytes ) const { Assert( pTargetItem ); Assert( pAttrDef ); TAttribInMemoryType typedValue; ConvertByteStreamToTypedValue( sBytes, &typedValue ); pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- template < typename TAttribInMemoryType > /*virtual*/ void ISchemaAttributeTypeBase::ConvertEconAttributeValueToByteStream( const attribute_data_union_t& value, ::std::string *out_psBytes ) const { ConvertTypedValueToByteStream( GetTypedValueContentsFromEconAttributeValue( value ), out_psBytes ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- template < typename TAttribInMemoryType > /*virtual*/ void ISchemaAttributeTypeBase::LoadEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value ) const { pTargetItem->SetDynamicAttributeValue( pAttrDef, GetTypedValueContentsFromEconAttributeValue( value ) ); } #ifdef GC_DLL //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- struct CEconItemEquipInstanceHelpers { static void AssignItemToSlot( CEconSharedObjectCache *pSOCache, CEconItem *pItem, equipped_class_t unClass, equipped_slot_t unSlot, CEconUserSession *pOptionalSession = NULL ); }; #endif // GC_DLL void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, CEconItem *pItem, uint32 unOwnerID, EItemAction eAction, uint32 unData ); void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData ); bool YieldingAddItemToDatabase( CEconItem *pItem, const CSteamID & steamID, EItemAction eAction, uint32 unData ); //----------------------------------------------------------------------------- // Purpose: wrap the idea of "get a loot list from this item"; some loot lists // are static definitions and some are temporary heap-allocated objects // and this means you don't care which you're dealing with until we // come up with a better interface //----------------------------------------------------------------------------- class CCrateLootListWrapper { public: CCrateLootListWrapper( const IEconItemInterface *pEconItem ) : m_pLootList( NULL ) , m_unAuditDetailData( 0 ) , m_bIsDynamicallyAllocatedLootList( false ) { Assert( pEconItem ); if ( !BAttemptCrateSeriesInitialization( pEconItem ) && !BAttemptLootListStringInitialization( pEconItem ) && !BAttemptLineItemInitialization( pEconItem ) ) { // We don't actually have anything to do here. We'll return NULL when someone asks for our // loot list and we're done. } } ~CCrateLootListWrapper() { if ( m_bIsDynamicallyAllocatedLootList ) { delete m_pLootList; } } const IEconLootList *GetEconLootList() const { return m_pLootList; } uint32 GetAuditDetailData() const { return m_unAuditDetailData; } private: CCrateLootListWrapper( const CCrateLootListWrapper& ); // intentionally unimplemented void operator=( const CCrateLootListWrapper& ); // intentionally unimplemented private: // Look for an attribute that specifies a crate series. MUST_CHECK_RETURN bool BAttemptCrateSeriesInitialization( const IEconItemInterface *pEconItem ); // Look for an attribute that specifies a loot list by string name. MUST_CHECK_RETURN bool BAttemptLootListStringInitialization( const IEconItemInterface *pEconItem ); // Look for a line-item-per-attribute list. MUST_CHECK_RETURN bool BAttemptLineItemInitialization( const IEconItemInterface *pEconItem ); private: const IEconLootList *m_pLootList; uint32 m_unAuditDetailData; bool m_bIsDynamicallyAllocatedLootList; }; //----------------------------------------------------------------------------- // Purpose: Maintains a handle to an CEconItem. If the item gets deleted, this // handle will return NULL when dereferenced //----------------------------------------------------------------------------- class CEconItemHandle : GCSDK::ISharedObjectListener { public: CEconItemHandle() : m_pItem( NULL ) , m_iItemID( INVALID_ITEM_ID ) {} CEconItemHandle( CEconItem* pItem ) : m_pItem( pItem ) { SetItem( pItem ); } virtual ~CEconItemHandle(); void SetItem( CEconItem* pItem ); operator CEconItem *( void ) const { return m_pItem; } CEconItem* operator->( void ) const { return m_pItem; } CEconItem* operator=( CEconItem* pRhs ) { SetItem( pRhs ); return m_pItem; } virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE; virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{} virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{} virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{} private: void UnsubscribeFromSOEvents(); CEconItem* m_pItem; // The item itemid_t m_iItemID; // The stored itemID CSteamID m_OwnerSteamID; // Steam ID of the item owner. Used for registering/unregistering from SOCache }; #endif // ECONITEM_H