Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

864 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: CEconItem, a shared object for econ items
  4. //
  5. //=============================================================================
  6. #ifndef ECONITEM_H
  7. #define ECONITEM_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "gcsdk/gcclientsdk.h"
  12. #include "base_gcmessages.pb.h"
  13. #include "econ_item_constants.h"
  14. #include "econ_item_interface.h"
  15. #include "econ_item_schema.h"
  16. #include <typeinfo> // needed for typeid()
  17. #define ENABLE_TYPED_ATTRIBUTE_PARANOIA 1
  18. #ifdef GC_DLL
  19. class CSchItem;
  20. class CEconSharedObjectCache;
  21. #endif
  22. namespace GCSDK
  23. {
  24. class CColumnSet;
  25. #ifdef GC_DLL
  26. class CWebAPIValues;
  27. #endif
  28. };
  29. class CEconItem;
  30. class CSOEconItem;
  31. class CEconItemCustomData;
  32. class CEconSessionItemAudit;
  33. //-----------------------------------------------------------------------------
  34. // Stats tracking for the attributes attached to CEconItem instances.
  35. //-----------------------------------------------------------------------------
  36. struct schema_attribute_stat_bucket_t
  37. {
  38. const schema_attribute_stat_bucket_t *m_pNext;
  39. const char *m_pszDesc;
  40. uint64 m_unLiveInlineCount;
  41. uint64 m_unLifetimeInlineCount;
  42. uint64 m_unLiveHeapCount;
  43. uint64 m_unLifetimeHeapCount;
  44. void OnAllocateInlineInstance() { m_unLiveInlineCount++; m_unLifetimeInlineCount++; }
  45. void OnFreeInlineInstance() { Assert( m_unLiveInlineCount > 0 ); m_unLiveInlineCount--; }
  46. void OnAllocateHeapInstance() { m_unLiveHeapCount++; m_unLifetimeHeapCount++; }
  47. void OnFreeHeapInstance() { Assert( m_unLiveHeapCount ); m_unLiveHeapCount--; }
  48. };
  49. class CSchemaAttributeStats
  50. {
  51. public:
  52. template < typename TAttribStatsStorageClass, typename TAttribInMemoryType >
  53. static void RegisterAttributeType()
  54. {
  55. TAttribStatsStorageClass::s_InstanceStats.m_pszDesc = typeid( TAttribInMemoryType ).name();
  56. TAttribStatsStorageClass::s_InstanceStats.m_pNext = m_pHead;
  57. m_pHead = &TAttribStatsStorageClass::s_InstanceStats;
  58. }
  59. static const schema_attribute_stat_bucket_t *GetFirstStatBucket()
  60. {
  61. return m_pHead;
  62. }
  63. private:
  64. static const schema_attribute_stat_bucket_t *m_pHead;
  65. };
  66. //-----------------------------------------------------------------------------
  67. // Base class interface for attributes of a certain in-memory type.
  68. //-----------------------------------------------------------------------------
  69. unsigned int Internal_GetAttributeTypeUniqueIdentifierNextValue();
  70. template < typename T >
  71. unsigned int GetAttributeTypeUniqueIdentifier()
  72. {
  73. static unsigned int s_unUniqueCounter = Internal_GetAttributeTypeUniqueIdentifierNextValue();
  74. return s_unUniqueCounter;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Base class interface for attributes of a certain in-memory type.
  78. //-----------------------------------------------------------------------------
  79. template < typename TAttribInMemoryType >
  80. class ISchemaAttributeTypeBase : public ISchemaAttributeType
  81. {
  82. friend class CSchemaAttributeStats;
  83. public:
  84. ISchemaAttributeTypeBase()
  85. {
  86. CSchemaAttributeStats::RegisterAttributeType< ISchemaAttributeTypeBase<TAttribInMemoryType>, TAttribInMemoryType >();
  87. // The implementation of the attributes-in-memory system is such that it may or may not behave according to
  88. // expectations. Rather than have to stare at all the details to answer questions about where memory is allocated
  89. // or managed, or when it will be freed, for all our current use cases it makes more sense to just disable raw
  90. // pointer types from being an attribute-in-memory type and instead steer people towards this message explaining
  91. // why.
  92. COMPILE_TIME_ASSERT( !IsPointerType<TAttribInMemoryType>::kValue );
  93. }
  94. #ifdef GC_DLL
  95. // By default, without a specific type we don't support any sort of custom value generation, so all we can do
  96. // to load an attribute is to copy the value out from the generic format (union) and turn it into whatever our
  97. // type is, and then add that type to the item as an attribute.
  98. //
  99. // Unlike most of the functions in this class, this is not meant to be a catch-all default implementation but
  100. // is instead a base implementation. Subclasses are intended to override to add or change functionality.
  101. virtual void LoadOrGenerateEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount ) const OVERRIDE
  102. {
  103. Assert( pTargetItem );
  104. Assert( pAttrDef );
  105. AssertMsg( !staticAttrib.m_pKVCustomData, "Default implementation of LoadOrGenerateEconAttributeValue() doesn't support custom value generation!" );
  106. 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!" );
  107. LoadEconAttributeValue( pTargetItem, pAttrDef, staticAttrib.m_value );
  108. }
  109. // By default, we dont generate any custom value
  110. virtual void GenerateEconAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const static_attrib_t& staticAttrib, const CEconGameAccount *pGameAccount, attribute_data_union_t* out_pValue ) const OVERRIDE
  111. {
  112. Assert( pAttrDef );
  113. Assert( pGameAccount );
  114. Assert( out_pValue );
  115. }
  116. #endif // GC_DLL
  117. virtual void LoadEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value ) const OVERRIDE;
  118. // Returns a unique identifier per run based on the type of <TAttribInMemoryType>.
  119. virtual unsigned int GetTypeUniqueIdentifier() const OVERRIDE
  120. {
  121. return GetAttributeTypeUniqueIdentifier<TAttribInMemoryType>();
  122. }
  123. // Takes the value specified in [typedValue] and stores it in the most appropriate way
  124. // somewhere attached to [out_pValue]. This may hit the heap. The storage itself is
  125. // intended to be opaque but can be reversed by calling GetTypedValueContentsFromEconAttributeValue().
  126. void ConvertTypedValueToEconAttributeValue( const TAttribInMemoryType& typedValue, attribute_data_union_t *out_pValue ) const
  127. {
  128. // If our type is smaller than an int, we don't know how to copy the memory into our flat structure. We could write
  129. // 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
  130. // they know where to fix.
  131. COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) );
  132. // Do we fit in the bottom 32-bits?
  133. if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
  134. {
  135. *reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asUint32 ) = typedValue;
  136. }
  137. // What about in the full 64-bits (if we're running a 64-bit build)?
  138. else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
  139. {
  140. *reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asBlobPointer ) = typedValue;
  141. }
  142. // We're too big for our flat structure. We need to allocate space somewhere outside our attribute instance and point
  143. // to that.
  144. else
  145. {
  146. Assert( out_pValue->asBlobPointer );
  147. *reinterpret_cast<TAttribInMemoryType *>( out_pValue->asBlobPointer ) = typedValue;
  148. }
  149. }
  150. // Guaranteed to return a valid reference (or assert/crash if calling code is behaving inappropriately and calling
  151. // this before an attribute value is allocated/set).
  152. const TAttribInMemoryType& GetTypedValueContentsFromEconAttributeValue( const attribute_data_union_t& value ) const
  153. {
  154. COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) );
  155. // Do we fit in the bottom 32-bits?
  156. if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
  157. return *reinterpret_cast<const TAttribInMemoryType *>( &value.asUint32 );
  158. // What about in the full 64-bits (if we're running a 64-bit build)?
  159. if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
  160. return *reinterpret_cast<const TAttribInMemoryType *>( &value.asBlobPointer );
  161. // We don't expect to get to a "read value" call without having written a value, which would
  162. // have allocated this memory.
  163. Assert( value.asBlobPointer );
  164. return *reinterpret_cast<const TAttribInMemoryType *>( value.asBlobPointer );
  165. }
  166. void ConvertEconAttributeValueToTypedValue( const attribute_data_union_t& value, TAttribInMemoryType *out_pTypedValue ) const
  167. {
  168. Assert( out_pTypedValue );
  169. *out_pTypedValue = GetTypedValueContentsFromEconAttributeValue( value );
  170. }
  171. void InitializeNewEconAttributeValue( attribute_data_union_t *out_pValue ) const OVERRIDE
  172. {
  173. if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
  174. {
  175. new( &out_pValue->asUint32 ) TAttribInMemoryType;
  176. s_InstanceStats.OnAllocateInlineInstance();
  177. }
  178. else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
  179. {
  180. new( &out_pValue->asBlobPointer ) TAttribInMemoryType;
  181. s_InstanceStats.OnAllocateInlineInstance();
  182. }
  183. else
  184. {
  185. out_pValue->asBlobPointer = reinterpret_cast<byte *>( new TAttribInMemoryType );
  186. s_InstanceStats.OnAllocateHeapInstance();
  187. }
  188. }
  189. virtual void UnloadEconAttributeValue( attribute_data_union_t *out_pValue ) const OVERRIDE
  190. {
  191. COMPILE_TIME_ASSERT( sizeof( TAttribInMemoryType ) >= sizeof( uint32 ) );
  192. // For smaller types, anything that fits inside the bits of a void pointer, we store the contents
  193. // inline and only have to worry about calling the correct destructor. We check against the small-/
  194. // size/medium-size values separately to not worry about which bits we're storing the uint32 in.
  195. if ( sizeof( TAttribInMemoryType ) <= sizeof( uint32 ) )
  196. {
  197. (reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asUint32 ))->~TAttribInMemoryType();
  198. s_InstanceStats.OnFreeInlineInstance();
  199. }
  200. else if ( sizeof( TAttribInMemoryType ) <= sizeof( void * ) )
  201. {
  202. (reinterpret_cast<TAttribInMemoryType *>( &out_pValue->asBlobPointer ))->~TAttribInMemoryType();
  203. s_InstanceStats.OnFreeInlineInstance();
  204. }
  205. // For larger types, we have the memory stored on the heap somewhere. We don't have to manually
  206. // destruct, but we do have to manually free.
  207. else
  208. {
  209. Assert( out_pValue->asBlobPointer );
  210. delete reinterpret_cast<TAttribInMemoryType *>( out_pValue->asBlobPointer );
  211. s_InstanceStats.OnFreeHeapInstance();
  212. }
  213. }
  214. virtual bool OnIterateAttributeValue( IEconItemAttributeIterator *pIterator, const CEconItemAttributeDefinition *pAttrDef, const attribute_data_union_t& value ) const OVERRIDE
  215. {
  216. Assert( pIterator );
  217. Assert( pAttrDef );
  218. // Call the appropriate virtual function on our iterator based on whatever type we represent.
  219. return pIterator->OnIterateAttributeValue( pAttrDef, GetTypedValueContentsFromEconAttributeValue( value ) );
  220. }
  221. virtual void LoadByteStreamToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const std::string& sBytes ) const OVERRIDE;
  222. virtual void ConvertEconAttributeValueToByteStream( const attribute_data_union_t& value, ::std::string *out_psBytes ) const;
  223. virtual void ConvertTypedValueToByteStream( const TAttribInMemoryType& typedValue, ::std::string *out_psBytes ) const = 0;
  224. virtual void ConvertByteStreamToTypedValue( const ::std::string& sBytes, TAttribInMemoryType *out_pTypedValue ) const = 0;
  225. private:
  226. static schema_attribute_stat_bucket_t s_InstanceStats;
  227. };
  228. // This function exists only to back-convert code that relies on the old untyped
  229. // attribute system, doing things like shoving floating-point bits into a uint32
  230. // value in the database.
  231. //
  232. // There is no reason to use this function moving forward! If you're writing new
  233. // code and calling this function seems like the only way to get the effect you
  234. // want, it probably just means that there is no attribute type for what you're
  235. // trying to do yet.
  236. template < typename T > uint32 WrapDeprecatedUntypedEconItemAttribute( T tValue ) { COMPILE_TIME_ASSERT( sizeof( T ) == sizeof( uint32 ) ); return *reinterpret_cast<uint32 *>( &tValue ); }
  237. template < typename TAttribInMemoryType >
  238. schema_attribute_stat_bucket_t ISchemaAttributeTypeBase<TAttribInMemoryType>::s_InstanceStats;
  239. class CEconItem : public GCSDK::CSharedObject, public CMaterialOverrideContainer< IEconItemInterface >
  240. {
  241. #ifdef GC_DLL
  242. DECLARE_CLASS_MEMPOOL( CEconItem );
  243. #endif
  244. public:
  245. typedef GCSDK::CSharedObject BaseClass;
  246. struct attribute_t
  247. {
  248. attrib_definition_index_t m_unDefinitionIndex; // stored as ints here for memory efficiency on the GC
  249. attribute_data_union_t m_value;
  250. private:
  251. void operator=( const attribute_t& rhs );
  252. };
  253. struct EquippedInstance_t
  254. {
  255. EquippedInstance_t() : m_unEquippedClass( 0 ), m_unEquippedSlot( INVALID_EQUIPPED_SLOT ) {}
  256. EquippedInstance_t( equipped_class_t unClass, equipped_slot_t unSlot ) : m_unEquippedClass( unClass ), m_unEquippedSlot( unSlot ) {}
  257. equipped_class_t m_unEquippedClass;
  258. equipped_slot_t m_unEquippedSlot;
  259. };
  260. #ifdef GC_DLL
  261. class CAuditEntry
  262. {
  263. public:
  264. CAuditEntry( EItemAction eAction, uint32 unData ) : m_eAction( eAction ), m_unData( unData ) { }
  265. bool BAddAuditEntryToTransaction( CSQLAccess& sqlAccess, const CEconItem *pItem ) const;
  266. private:
  267. EItemAction m_eAction;
  268. uint32 m_unData;
  269. };
  270. // Set only the top 16 bits for field ID types! These will be or'd into the index of
  271. // the field itself and then pulled apart later.
  272. enum
  273. {
  274. kUpdateFieldIDType_FieldID = 0x00000000, // this must stay as 0 for legacy code
  275. kUpdateFieldIDType_AttributeID = 0x00010000,
  276. };
  277. #endif // GC_DLL
  278. const static int k_nTypeID = k_EEconTypeItem;
  279. virtual int GetTypeID() const { return k_nTypeID; }
  280. CEconItem();
  281. CEconItem( const CEconItem& rhs );
  282. virtual ~CEconItem();
  283. CEconItem &operator=( const CEconItem& rhs );
  284. //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
  285. //needed since the base implementation of this is protected
  286. RTime32 GetTradableAfterDateTime() const { return IEconItemInterface::GetTradableAfterDateTime(); }
  287. //called to set a tradable after date/time value onto this item (this avoids a lot of potential inefficiencies around this process)
  288. void SetTradableAfterDateTime( RTime32 rtTime );
  289. // IEconItemInterface interface.
  290. const GameItemDefinition_t *GetItemDefinition() const;
  291. public:
  292. virtual void IterateAttributes( class IEconItemAttributeIterator *pIterator ) const OVERRIDE;
  293. virtual itemid_t GetID() const { return GetItemID(); }
  294. // Accessors/Settors
  295. itemid_t GetItemID() const { return m_ulID; }
  296. void SetItemID( uint64 ulID );
  297. itemid_t GetOriginalID() const;
  298. void SetOriginalID( uint64 ulOriginalID );
  299. uint32 GetAccountID() const { return m_unAccountID; }
  300. void SetAccountID( uint32 unAccountID ) { m_unAccountID = unAccountID; }
  301. uint32 GetDefinitionIndex() const { return m_unDefIndex; }
  302. void SetDefinitionIndex( uint32 unDefinitionIndex ) { m_unDefIndex = unDefinitionIndex; }
  303. uint32 GetItemLevel() const { return m_unLevel; }
  304. void SetItemLevel( uint32 unItemLevel ) { m_unLevel = unItemLevel; }
  305. int32 GetQuality() const { return m_nQuality; }
  306. void SetQuality( int32 nQuality ) { m_nQuality = nQuality; }
  307. uint32 GetInventoryToken() const { return m_unInventory; }
  308. void SetInventoryToken( uint32 unToken ) { m_unInventory = unToken; }
  309. int GetQuantity() const;
  310. void SetQuantity( uint16 unQuantity );
  311. uint8 GetFlags() const { return m_unFlags; }
  312. void SetFlags( uint8 unFlags ) { m_unFlags = unFlags; }
  313. void SetFlag( uint8 unFlag ) { m_unFlags |= unFlag; }
  314. void ClearFlag( uint8 unFlag ) { m_unFlags &= ~unFlag; }
  315. bool CheckFlags( uint8 unFlags ) const { return ( m_unFlags & unFlags ) != 0; }
  316. eEconItemOrigin GetOrigin() const { return (eEconItemOrigin)m_unOrigin; }
  317. void SetOrigin( eEconItemOrigin unOrigin ) { m_unOrigin = unOrigin; Assert( m_unOrigin == unOrigin ); }
  318. bool IsForeign() const { return m_unOrigin == kEconItemOrigin_Foreign; }
  319. style_index_t GetStyle() const;
  320. void SetStyle( uint8 unStyle ) { m_unStyle = unStyle; DirtyIconURL(); }
  321. const char *GetIconURLSmall() const;
  322. const char *GetIconURLLarge() const;
  323. const char *GetCustomName() const;
  324. void SetCustomName( const char *pName );
  325. const char *GetCustomDesc() const;
  326. void SetCustomDesc( const char *pDesc );
  327. bool IsEquipped() const;
  328. bool IsEquippedForClass( equipped_class_t unClass ) const;
  329. equipped_slot_t GetEquippedPositionForClass( equipped_class_t unClass ) const;
  330. void Equip( equipped_class_t unClass, equipped_slot_t unSlot );
  331. void Unequip();
  332. void UnequipFromClass( equipped_class_t unClass );
  333. // This should really only used for the WebAPIs, debugging, etc. Data manipulation during gameplay should use
  334. // the above functions.
  335. int GetEquippedInstanceCount() const;
  336. const EquippedInstance_t &GetEquippedInstance( int iIdx ) const;
  337. virtual bool GetInUse() const;
  338. void SetInUse( bool bInUse );
  339. bool IsTradable() const;
  340. bool IsMarketable() const;
  341. bool IsCommodity() const;
  342. void AdoptMoreRestrictedTradabilityFromItem( const CEconItem *pOther, uint32 nTradabilityFlagsToAccept = 0xFFFFFFFF );
  343. void AdoptMoreRestrictedTradability( uint32 nTradabilityFlags, RTime32 nUntradableTime );
  344. bool IsUsableInCrafting() const;
  345. #ifdef GC_DLL
  346. RTime32 GetAssetInfoExpirationCacheExpirationTime() const;
  347. #endif // GC_DLL
  348. // --------------------------------------------------------------------------------------------
  349. // Typed attributes. These are methods for accessing and setting values of attributes with
  350. // some semblance of type information and type safety.
  351. // --------------------------------------------------------------------------------------------
  352. // Assign the value of the attribute [pAttrDef] to [value]. Passing in a type for [value] that
  353. // doesn't match the storage type specified by the attribute definition will fail asserts a bunch
  354. // of asserts all the way down the stack and may or may not crash -- it would be nice to make this
  355. // fail asserts at compile time.
  356. //
  357. // This function has undefined results (besides asserting) if called to add a dynamic version of
  358. // an attrib that's already specified statically.
  359. template < typename T >
  360. void SetDynamicAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const T& value )
  361. {
  362. Assert( pAttrDef );
  363. const ISchemaAttributeTypeBase<T> *pAttrType = GetTypedAttributeType<T>( pAttrDef );
  364. #ifdef GC_DLL
  365. // The GC is expected to always have internally-consistent information and so be able to access the
  366. // type information of any attribute if we started up successfully.
  367. Assert( pAttrType );
  368. #else
  369. // Game clients and servers may be running code that doesn't have all of the types for the new attributes
  370. // for a GC that just propped. Because we're not authoritative over items here, about the best we can do
  371. // here is abort entirely. This means that the client may not display certain attributes at all, or even
  372. // have them in the attribute list in memory, but we don't understand those attributes anyway.
  373. if ( !pAttrType )
  374. return;
  375. #endif
  376. // Fail right off the bat if we're trying to write a dynamic attribute value for an item that already
  377. // has this as a static value.
  378. AssertMsg4( !::FindAttribute( GetItemDefinition(), pAttrDef ),
  379. "Item id %llu (%s) attempting to set dynamic attribute value for '%s' (%d) when static attribute exists!",
  380. GetItemID(), GetItemDefinition()->GetDefinitionName(), pAttrDef->GetDefinitionName(), pAttrDef->GetDefinitionIndex() );
  381. // Alright, we have a data type match so we can safely store data. Some types may need to initialize
  382. // their data to a current state if it's the first time we're writing to this value (as opposed to
  383. // updating an existing value).
  384. attribute_t *pEconAttrib = FindDynamicAttributeInternal( pAttrDef );
  385. if ( !pEconAttrib )
  386. {
  387. pEconAttrib = &(AddDynamicAttributeInternal());
  388. pEconAttrib->m_unDefinitionIndex = pAttrDef->GetDefinitionIndex();
  389. pAttrType->InitializeNewEconAttributeValue( &pEconAttrib->m_value );
  390. }
  391. pAttrType->ConvertTypedValueToEconAttributeValue( value, &pEconAttrib->m_value );
  392. #if ENABLE_TYPED_ATTRIBUTE_PARANOIA
  393. // Paranoia!: make sure that our read/write functions are mirrored correctly, and that if we attempt
  394. // to read back a value we get something identical to what we just wrote. We do this via converting
  395. // to strings and then comparing those because there may or not be equality comparisons for our type
  396. // T that make sense (ie., protobufs).
  397. {
  398. T readValue;
  399. DbgVerify( FindAttribute( pAttrDef, &readValue ) );
  400. std::string sBytes, sReadBytes;
  401. pAttrType->ConvertTypedValueToByteStream( value, &sBytes );
  402. pAttrType->ConvertTypedValueToByteStream( readValue, &sReadBytes );
  403. AssertMsg1( sBytes == sReadBytes, "SetDynamicAttributeValue(): read/write mismatch for attribute '%s'.", pAttrDef->GetDefinitionName() );
  404. }
  405. #endif // ENABLE_TYPED_ATTRIBUTE_PARANOIA
  406. }
  407. // 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
  408. // only set it if this new time extends beyond the current one
  409. void SetDynamicMaxTimeAttributeValue( const CEconItemAttributeDefinition *pAttrDef, RTime32 rtTime );
  410. // Remove an instance of an attribute from this item. This will also free any dynamic memory associated
  411. // with that instance if any was allocated.
  412. void RemoveDynamicAttribute( const CEconItemAttributeDefinition *pAttrDef );
  413. // Copy all attributes and values in a type-safe way from [source] to ourself. Attributes that we have
  414. // that don't exist on [source] will maintain their current values. All other attributes will get their
  415. // values set to whatever [source] specifies.
  416. void CopyAttributesFrom( const CEconItem& source );
  417. bool BHasDynamicAttributes() const { return GetDynamicAttributeCountInternal() > 0; }
  418. private:
  419. const char* FindIconURL( bool bLarge ) const;
  420. void Init();
  421. template < typename T >
  422. static const ISchemaAttributeTypeBase<T> *GetTypedAttributeType( const CEconItemAttributeDefinition *pAttrDef )
  423. {
  424. // Make sure the type of data we're passing in matches the type of data we're claiming that we can
  425. // store in the attribute definition.
  426. const ISchemaAttributeType *pIAttr = pAttrDef->GetAttributeType();
  427. Assert( pIAttr );
  428. Assert( pIAttr->GetTypeUniqueIdentifier() == GetAttributeTypeUniqueIdentifier<T>() );
  429. #if ENABLE_TYPED_ATTRIBUTE_PARANOIA
  430. return dynamic_cast<const ISchemaAttributeTypeBase<T> *>( pIAttr );
  431. #else
  432. return static_cast<const ISchemaAttributeTypeBase<T> *>( pIAttr );
  433. #endif
  434. }
  435. public:
  436. void Compact();
  437. #ifdef GC
  438. bool BDeserializeFromKV( KeyValues *pKVItem, CUtlVector<CUtlString> *pVecErrors );
  439. #endif // GC
  440. #ifdef GC_DLL
  441. void ExportToAPI( GCSDK::CWebAPIValues *pValues ) const;
  442. bool BImportFromAPI( GCSDK::CWebAPIValues *pValues );
  443. #endif // GC_DLL
  444. // these are overridden to handle attributes
  445. #ifdef GC_DLL
  446. virtual bool BYieldingAddInsertToTransaction( GCSDK::CSQLAccess & sqlAccess );
  447. virtual bool BYieldingAddWriteToTransaction( GCSDK::CSQLAccess & sqlAccess, const CUtlVector< int > &fields );
  448. virtual bool BYieldingAddRemoveToTransaction( GCSDK::CSQLAccess & sqlAccess );
  449. void SerializeToSchemaItem( CSchItem &item ) const;
  450. void DeserializeFromSchemaItem( const CSchItem &item );
  451. void SetInteriorItem( CEconItem* pInteriorItem );
  452. #endif // GC_DLL
  453. virtual bool BParseFromMessage( const CUtlBuffer &buffer ) OVERRIDE;
  454. virtual bool BParseFromMessage( const std::string &buffer ) OVERRIDE;
  455. virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) OVERRIDE;
  456. #ifdef GC
  457. virtual bool BAddToMessage( CUtlBuffer & bufOutput ) const OVERRIDE;
  458. virtual bool BAddToMessage( std::string *pBuffer ) const OVERRIDE; // short cut to remove an extra copy
  459. virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const OVERRIDE;
  460. virtual bool BAddDestroyToMessage( std::string *pBuffer ) const OVERRIDE;
  461. bool BYieldingSerializeFromDatabase( itemid_t ulItemID );
  462. #endif
  463. virtual bool BIsKeyLess( const CSharedObject & soRHS ) const ;
  464. virtual void Copy( const CSharedObject & soRHS );
  465. virtual void Dump() const;
  466. virtual CUtlString GetDebugString() const OVERRIDE;
  467. void SerializeToProtoBufItem( CSOEconItem &msgItem ) const;
  468. void DeserializeFromProtoBufItem( const CSOEconItem &msgItem );
  469. #ifdef GC_DLL
  470. CEconItem* YieldingGetInteriorItem();
  471. const CEconItem* YieldingGetInteriorItem() const { return const_cast<CEconItem *>(this)->YieldingGetInteriorItem(); }
  472. void SetEquippedThisGameServerSession( bool bEquipped ) { m_bEquippedThisGameServerSession = bEquipped; }
  473. bool EquippedThisGameServerSession() const { return m_bEquippedThisGameServerSession; }
  474. #endif
  475. // Non-yielding -- will return current interior item if it exists and is already loaded
  476. // but will make no attempt to load.
  477. CEconItem* GetInteriorItem();
  478. const CEconItem* GetInteriorItem() const { return const_cast<CEconItem *>(this)->GetInteriorItem(); }
  479. const CEconItemCustomData* GetCustomData() const { return m_pCustomData; }
  480. void OnTraded( uint32 unTradabilityDelaySeconds );
  481. void OnReceivedFromMarket( bool bFromRollback );
  482. protected:
  483. // Call this when the appearance of this item changes (ex. paintkit, style, festive). This will
  484. // cause the icon to be lazily re-evaluated (ie. so that changing the style will change the icon)
  485. void DirtyIconURL() { m_pszLargeIcon = NULL; m_pszSmallIcon = NULL; }
  486. // CSharedObject
  487. // adapted from CSchemaSharedObject
  488. void GetDirtyColumnSet( const CUtlVector< int > &fields, GCSDK::CColumnSet &cs ) const;
  489. void EnsureCustomDataExists();
  490. bool BYieldingLoadInteriorItem();
  491. void OnTransferredOwnership();
  492. // Internal attribute interface.
  493. friend class CWebAPIStringExporterAttributeIterator;
  494. friend class CAttributeToStringIterator;
  495. attribute_t& AddDynamicAttributeInternal(); // add another chunk of data to our internal storage to store a new attribute -- initialization is the responsibility of the caller
  496. 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
  497. int GetDynamicAttributeCountInternal() const; // how many attributes are there attached to this instance?
  498. 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)
  499. const attribute_t& GetDynamicAttributeInternal( int iAttrIndexIntoArray ) const // read-only version of our attribute memory base chunk for this index (same "array" as GetDynamicAttributeCountInternal)
  500. {
  501. return const_cast<CEconItem *>( this )->GetMutableDynamicAttributeInternal( iAttrIndexIntoArray );
  502. }
  503. const EquippedInstance_t *FindEquippedInstanceForClass( equipped_class_t nClass ) const;
  504. void InternalVerifyEquipInstanceIntegrity() const;
  505. struct dirty_bits_t
  506. {
  507. // other
  508. uint8 m_bInUse : 1;
  509. uint8 m_bHasEquipSingleton : 1;
  510. uint8 m_bHasAttribSingleton: 1;
  511. };
  512. mutable const char* m_pszSmallIcon;
  513. mutable const char* m_pszLargeIcon;
  514. public:
  515. // data that is most commonly changed
  516. uint64 m_ulID; // Item ID
  517. uint32 m_unAccountID; // Item Owner
  518. uint32 m_unInventory; // App managed int representing inventory placement
  519. item_definition_index_t m_unDefIndex; // Item definition index
  520. uint8 m_unLevel; // Item Level
  521. uint8 m_nQuality; // Item quality (rarity)
  522. uint8 m_unFlags; // Flags
  523. uint8 m_unOrigin; // Origin (eEconItemOrigin)
  524. style_index_t m_unStyle; // Style
  525. dirty_bits_t m_dirtyBits; // dirty bits
  526. // Fields that we often have zero or one of, but not often more
  527. EquippedInstance_t m_EquipInstanceSingleton; // Where the item is equipped. Valid only if m_bHasEquipSingleton and there is no custom data
  528. attribute_t m_CustomAttribSingleton; // Custom attribute. Valid only if m_bHasAttribSingleton and there is no custom data
  529. // optional data (custom name, additional attributes, etc.)
  530. CEconItemCustomData *m_pCustomData;
  531. #ifdef GC_DLL
  532. private:
  533. bool m_bEquippedThisGameServerSession;
  534. #endif // GC_DLL
  535. };
  536. //-----------------------------------------------------------------------------
  537. // Purpose: Storage for data that is not commonly changed in CEconItem, primarily
  538. // as a memory savings mechanism.
  539. //-----------------------------------------------------------------------------
  540. class CEconItemCustomData
  541. {
  542. public:
  543. CEconItemCustomData()
  544. : m_pInteriorItem( NULL )
  545. , m_ulOriginalID( INVALID_ITEM_ID )
  546. , m_unQuantity( 1 )
  547. , m_vecAttributes( /* grow size: */ 1, /* init size: */ 0 )
  548. , m_vecEquipped( /* grow size: */ 1, /* init size: */ 0 )
  549. {}
  550. ~CEconItemCustomData();
  551. CUtlVector< CEconItem::attribute_t > m_vecAttributes;
  552. CEconItem* m_pInteriorItem;
  553. uint64 m_ulOriginalID; // Original Item ID
  554. uint16 m_unQuantity; // Consumable stack count (ammo, money, etc)
  555. CUtlVector<CEconItem::EquippedInstance_t> m_vecEquipped;
  556. static void FreeAttributeMemory( CEconItem::attribute_t *pAttrib );
  557. #ifdef GC_DLL
  558. DECLARE_CLASS_MEMPOOL( CEconItemCustomData );
  559. #endif
  560. };
  561. //-----------------------------------------------------------------------------
  562. // Purpose:
  563. //-----------------------------------------------------------------------------
  564. template < typename TAttribInMemoryType >
  565. /*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::LoadByteStreamToEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const std::string& sBytes ) const
  566. {
  567. Assert( pTargetItem );
  568. Assert( pAttrDef );
  569. TAttribInMemoryType typedValue;
  570. ConvertByteStreamToTypedValue( sBytes, &typedValue );
  571. pTargetItem->SetDynamicAttributeValue( pAttrDef, typedValue );
  572. }
  573. //-----------------------------------------------------------------------------
  574. // Purpose:
  575. //-----------------------------------------------------------------------------
  576. template < typename TAttribInMemoryType >
  577. /*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::ConvertEconAttributeValueToByteStream( const attribute_data_union_t& value, ::std::string *out_psBytes ) const
  578. {
  579. ConvertTypedValueToByteStream( GetTypedValueContentsFromEconAttributeValue( value ), out_psBytes );
  580. }
  581. //-----------------------------------------------------------------------------
  582. // Purpose:
  583. //-----------------------------------------------------------------------------
  584. template < typename TAttribInMemoryType >
  585. /*virtual*/ void ISchemaAttributeTypeBase<TAttribInMemoryType>::LoadEconAttributeValue( CEconItem *pTargetItem, const CEconItemAttributeDefinition *pAttrDef, const union attribute_data_union_t& value ) const
  586. {
  587. pTargetItem->SetDynamicAttributeValue( pAttrDef, GetTypedValueContentsFromEconAttributeValue( value ) );
  588. }
  589. #ifdef GC_DLL
  590. //-----------------------------------------------------------------------------
  591. // Purpose:
  592. //-----------------------------------------------------------------------------
  593. struct CEconItemEquipInstanceHelpers
  594. {
  595. static void AssignItemToSlot( CEconSharedObjectCache *pSOCache, CEconItem *pItem, equipped_class_t unClass, equipped_slot_t unSlot, CEconUserSession *pOptionalSession = NULL );
  596. };
  597. #endif // GC_DLL
  598. void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, CEconItem *pItem, uint32 unOwnerID, EItemAction eAction, uint32 unData );
  599. void YieldingAddAuditRecord( GCSDK::CSQLAccess *sqlAccess, uint64 ulItemID, uint32 unOwnerID, EItemAction eAction, uint32 unData );
  600. bool YieldingAddItemToDatabase( CEconItem *pItem, const CSteamID & steamID, EItemAction eAction, uint32 unData );
  601. //-----------------------------------------------------------------------------
  602. // Purpose: wrap the idea of "get a loot list from this item"; some loot lists
  603. // are static definitions and some are temporary heap-allocated objects
  604. // and this means you don't care which you're dealing with until we
  605. // come up with a better interface
  606. //-----------------------------------------------------------------------------
  607. class CCrateLootListWrapper
  608. {
  609. public:
  610. CCrateLootListWrapper( const IEconItemInterface *pEconItem )
  611. : m_pLootList( NULL )
  612. , m_unAuditDetailData( 0 )
  613. , m_bIsDynamicallyAllocatedLootList( false )
  614. {
  615. Assert( pEconItem );
  616. if ( !BAttemptCrateSeriesInitialization( pEconItem )
  617. && !BAttemptLootListStringInitialization( pEconItem )
  618. && !BAttemptLineItemInitialization( pEconItem ) )
  619. {
  620. // We don't actually have anything to do here. We'll return NULL when someone asks for our
  621. // loot list and we're done.
  622. }
  623. }
  624. ~CCrateLootListWrapper()
  625. {
  626. if ( m_bIsDynamicallyAllocatedLootList )
  627. {
  628. delete m_pLootList;
  629. }
  630. }
  631. const IEconLootList *GetEconLootList() const
  632. {
  633. return m_pLootList;
  634. }
  635. uint32 GetAuditDetailData() const
  636. {
  637. return m_unAuditDetailData;
  638. }
  639. private:
  640. CCrateLootListWrapper( const CCrateLootListWrapper& ); // intentionally unimplemented
  641. void operator=( const CCrateLootListWrapper& ); // intentionally unimplemented
  642. private:
  643. // Look for an attribute that specifies a crate series.
  644. MUST_CHECK_RETURN bool BAttemptCrateSeriesInitialization( const IEconItemInterface *pEconItem );
  645. // Look for an attribute that specifies a loot list by string name.
  646. MUST_CHECK_RETURN bool BAttemptLootListStringInitialization( const IEconItemInterface *pEconItem );
  647. // Look for a line-item-per-attribute list.
  648. MUST_CHECK_RETURN bool BAttemptLineItemInitialization( const IEconItemInterface *pEconItem );
  649. private:
  650. const IEconLootList *m_pLootList;
  651. uint32 m_unAuditDetailData;
  652. bool m_bIsDynamicallyAllocatedLootList;
  653. };
  654. //-----------------------------------------------------------------------------
  655. // Purpose: Maintains a handle to an CEconItem. If the item gets deleted, this
  656. // handle will return NULL when dereferenced
  657. //-----------------------------------------------------------------------------
  658. class CEconItemHandle : GCSDK::ISharedObjectListener
  659. {
  660. public:
  661. CEconItemHandle()
  662. : m_pItem( NULL )
  663. , m_iItemID( INVALID_ITEM_ID )
  664. {}
  665. CEconItemHandle( CEconItem* pItem )
  666. : m_pItem( pItem )
  667. {
  668. SetItem( pItem );
  669. }
  670. virtual ~CEconItemHandle();
  671. void SetItem( CEconItem* pItem );
  672. operator CEconItem *( void ) const
  673. {
  674. return m_pItem;
  675. }
  676. CEconItem* operator->( void ) const
  677. {
  678. return m_pItem;
  679. }
  680. CEconItem* operator=( CEconItem* pRhs )
  681. {
  682. SetItem( pRhs );
  683. return m_pItem;
  684. }
  685. virtual void SODestroyed( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  686. virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  687. virtual void SOCreated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  688. virtual void SOUpdated( const CSteamID & steamIDOwner, const GCSDK::CSharedObject *pObject, GCSDK::ESOCacheEvent eEvent ) OVERRIDE;
  689. virtual void PreSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{}
  690. virtual void PostSOUpdate( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{}
  691. virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, GCSDK::ESOCacheEvent eEvent ) OVERRIDE{}
  692. private:
  693. void UnsubscribeFromSOEvents();
  694. CEconItem* m_pItem; // The item
  695. itemid_t m_iItemID; // The stored itemID
  696. CSteamID m_OwnerSteamID; // Steam ID of the item owner. Used for registering/unregistering from SOCache
  697. };
  698. #endif // ECONITEM_H