summaryrefslogtreecommitdiff
path: root/game/shared/econ/econ_item.h
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/econ/econ_item.h')
-rw-r--r--game/shared/econ/econ_item.h864
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