diff options
Diffstat (limited to 'game/shared/econ/econ_item.h')
| -rw-r--r-- | game/shared/econ/econ_item.h | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/game/shared/econ/econ_item.h b/game/shared/econ/econ_item.h new file mode 100644 index 0000000..ae3270e --- /dev/null +++ b/game/shared/econ/econ_item.h @@ -0,0 +1,864 @@ +//========= 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 <typeinfo> // 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>, 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<TAttribInMemoryType>::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 <TAttribInMemoryType>. + virtual unsigned int GetTypeUniqueIdentifier() const OVERRIDE + { + return GetAttributeTypeUniqueIdentifier<TAttribInMemoryType>(); + } + + // 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<TAttribInMemoryType *>( &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<TAttribInMemoryType *>( &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<TAttribInMemoryType *>( 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<const TAttribInMemoryType *>( &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<const TAttribInMemoryType *>( &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<const TAttribInMemoryType *>( 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<byte *>( 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<TAttribInMemoryType *>( &out_pValue->asUint32 ))->~TAttribInMemoryType(); + s_InstanceStats.OnFreeInlineInstance(); + } + else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) ) + { + (reinterpret_cast<TAttribInMemoryType *>( &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<TAttribInMemoryType *>( 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<uint32 *>( &tValue ); } + +template < typename TAttribInMemoryType > +schema_attribute_stat_bucket_t ISchemaAttributeTypeBase<TAttribInMemoryType>::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<T> *pAttrType = GetTypedAttributeType<T>( 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<T> *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<T>() ); + +#if ENABLE_TYPED_ATTRIBUTE_PARANOIA + return dynamic_cast<const ISchemaAttributeTypeBase<T> *>( pIAttr ); +#else + return static_cast<const ISchemaAttributeTypeBase<T> *>( pIAttr ); +#endif + } + +public: + void Compact(); + +#ifdef GC + bool BDeserializeFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *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<CEconItem *>(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<CEconItem *>(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<CEconItem *>( 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<CEconItem::EquippedInstance_t> m_vecEquipped; + + static void FreeAttributeMemory( CEconItem::attribute_t *pAttrib ); + +#ifdef GC_DLL + DECLARE_CLASS_MEMPOOL( CEconItemCustomData ); +#endif +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template < typename TAttribInMemoryType > +/*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::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<TAttribInMemoryType>::ConvertEconAttributeValueToByteStream( const attribute_data_union_t& value, ::std::string *out_psBytes ) const +{ + ConvertTypedValueToByteStream( GetTypedValueContentsFromEconAttributeValue( value ), out_psBytes ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +template < typename TAttribInMemoryType > +/*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::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 |