//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Additional shared object cache functionality for the GC // //============================================================================= #ifndef GCCLIENT_SHAREDOBJECTCACHE_H #define GCCLIENT_SHAREDOBJECTCACHE_H #ifdef _WIN32 #pragma once #endif #include "sharedobjectcache.h" class CMsgSOCacheSubscribed; class CMsgSOCacheSubscribed_SubscribedType; namespace GCSDK { /// Enumerate different events that might trigger a callback to an ISharedObjectListener enum ESOCacheEvent { /// Dummy sentinel value eSOCacheEvent_None = 0, /// We received a our first update from the GC and are subscribed eSOCacheEvent_Subscribed = 1, /// We lost connection to GC or GC notified us that we are no longer subscribed. /// Objects stay in the cache, but we no longer receive updates eSOCacheEvent_Unsubscribed = 2, /// We received a full update from the GC on a cache for which we were already subscribed. /// This can happen if connectivity is lost, and then restored before we realized it was lost. eSOCacheEvent_Resubscribed = 3, /// We received an incremental update from the GC about specific object(s) being /// added, updated, or removed from the cache eSOCacheEvent_Incremental = 4, /// A lister was added to the cache /// @see CGCClientSharedObjectCache::AddListener eSOCacheEvent_ListenerAdded = 5, /// A lister was removed from the cache /// @see CGCClientSharedObjectCache::RemoveListener eSOCacheEvent_ListenerRemoved = 6, }; //---------------------------------------------------------------------------- // Purpose: Allow game components to register themselves to hear about inventory // changes when they are received from the server //---------------------------------------------------------------------------- class ISharedObjectListener { public: /// Called when a new object is created in a cache we are currently subscribed to, or when we are added /// as a listener to a cache which already has objects in it /// /// eEvent will be one of: /// - eSOCacheEvent_Subscribed /// - eSOCacheEvent_Resubscribed /// - eSOCacheEvent_Incremental /// - eSOCacheEvent_ListenerAdded virtual void SOCreated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) = 0; /// Called when we are about to update objects in our cache but haven't done any of the work yet. /// /// eEvent will be one of: /// - eSOCacheEvent_Resubscribed /// - eSOCacheEvent_Incremental virtual void PreSOUpdate( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) = 0; /// Called when an object is updated in a cache we are currently subscribed to. /// /// eEvent will be one of: /// - eSOCacheEvent_Resubscribed /// - eSOCacheEvent_Incremental virtual void SOUpdated( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) = 0; /// Called when we're finished updating objects in our cache for this batch. /// /// eEvent will be one of: /// - eSOCacheEvent_Resubscribed /// - eSOCacheEvent_Incremental virtual void PostSOUpdate( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) = 0; /// Called when an object is about to be deleted in a cache we are currently subscribed to. /// The object will have already been removed from the cache, but is still valid. /// /// eEvent will be one of: /// - eSOCacheEvent_Incremental /// - eSOCacheEvent_Resubscribed virtual void SODestroyed( const CSteamID & steamIDOwner, const CSharedObject *pObject, ESOCacheEvent eEvent ) = 0; /// Called to notify a listener that he is subscribed to the cache. /// /// eEvent will be one of: /// - eSOCacheEvent_Subscribed /// - eSOCacheEvent_Resubscribed /// - eSOCacheEvent_ListenerAdded /// /// A listener is guaranteed that it will not receive incremental updates (SOCreated, /// SOUpdated, SODestroyed) while not subscribed. (Before the SOCacheSubscribed or /// after SOCacheUnsubscribed.) However, note that it may be possible to receive /// an SOCacheSubscribed message while already subscribed. This can happen if the /// GC loses and restores connection, or otherwise decides that a full update is /// necessary. virtual void SOCacheSubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) = 0; /// Called to notify a listener that he is no longer subscribed to the cache. /// if he is being removed as a listener, then he will no longer receive /// updates. Otherwise, he might receive another SOCacheSubscribed /// message, followed by further update notifications. /// /// eEvent will be one of: /// - eSOCacheEvent_Unsubscribed /// - eSOCacheEvent_ListenerRemoved virtual void SOCacheUnsubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) = 0; }; //---------------------------------------------------------------------------- // Purpose: The part of a shared object cache that handles all objects of a // single type. //---------------------------------------------------------------------------- class CGCClientSharedObjectContext { public: CGCClientSharedObjectContext( const CSteamID & steamIDOwner ); bool BAddListener( ISharedObjectListener *pListener ); bool BRemoveListener( ISharedObjectListener *pListener ); void SOCreated( const CSharedObject *pObject, ESOCacheEvent eEvent ) const; void PreSOUpdate( GCSDK::ESOCacheEvent eEvent ) const; void SOUpdated( const CSharedObject *pObject, ESOCacheEvent eEvent ) const; void PostSOUpdate( GCSDK::ESOCacheEvent eEvent ) const; void SODestroyed( const CSharedObject *pObject, ESOCacheEvent eEvent ) const; void SOCacheSubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) const; void SOCacheUnsubscribed( const CSteamID & steamIDOwner, ESOCacheEvent eEvent ) const; const CSteamID & GetOwner() const { return m_steamIDOwner; } const CUtlVector< ISharedObjectListener * > & GetListeners() const { return m_vecListeners; } private: CSteamID m_steamIDOwner; CUtlVector m_vecListeners; }; //---------------------------------------------------------------------------- // Purpose: The part of a shared object cache that handles all objects of a // single type. //---------------------------------------------------------------------------- class CGCClientSharedObjectTypeCache : public CSharedObjectTypeCache { public: CGCClientSharedObjectTypeCache( int nTypeID, const CGCClientSharedObjectContext & context ); virtual ~CGCClientSharedObjectTypeCache(); bool BParseCacheSubscribedMsg( const CMsgSOCacheSubscribed_SubscribedType & msg, CUtlVector &vecCreatedObjects, CUtlVector &vecUpdatedObjects, CUtlVector &vecObjectsToDestroy ); CSharedObject *BCreateFromMsg( const void *pvData, uint32 unSize, bool *bUpdatedExisting ); bool BDestroyFromMsg( const void *pvData, uint32 unSize ); bool BUpdateFromMsg( const void *pvData, uint32 unSize ); void RemoveAllObjects( CUtlVector &vecObjects ); private: const CGCClientSharedObjectContext & m_context; }; //---------------------------------------------------------------------------- // Purpose: A cache of a bunch of shared objects of different types. This class // is shared between clients, gameservers, and the GC and is // responsible for sending messages from the GC to cause object // creation/destruction/updating on the clients/gameservers. //---------------------------------------------------------------------------- class CGCClientSharedObjectCache : public CSharedObjectCache { friend class CGCSOUpdateMultipleJob; public: CGCClientSharedObjectCache( const CSteamID & steamIDOwner = CSteamID() ); virtual ~CGCClientSharedObjectCache(); /// Have we received at least one update from the GC? bool BIsInitialized() const { return m_bInitialized; } /// Are we currently subscribed to updates from the GC? bool BIsSubscribed() const { return m_bSubscribed; } /// Who owns this cache? virtual const CSteamID & GetOwner() const { return m_context.GetOwner(); } /// Adds a listener interface to the object. A call to re-add /// a listener that's already listening is harmlessly ignored. /// /// If the cache is currently subscribed, then the listener will /// immediately receive a SOCacheSubscribed call. void AddListener( ISharedObjectListener *pListener ); /// Removes a listener interface on the object. Returns true /// if we were listening and removed OK, false if we were not /// previously listening and the call was ignored /// /// If the cache is currently subscribed and we were listening, /// then the listener will immediately receive a SOCacheUnsubscribed /// call. bool RemoveListener( ISharedObjectListener *pListener ); CGCClientSharedObjectTypeCache *FindTypeCache( int nClassID ) { return (CGCClientSharedObjectTypeCache *)FindBaseTypeCache( nClassID ); } CGCClientSharedObjectTypeCache *CreateTypeCache( int nClassID ) { return (CGCClientSharedObjectTypeCache *)CreateBaseTypeCache( nClassID ); } bool BParseCacheSubscribedMsg( const CMsgSOCacheSubscribed & msg ); void NotifyUnsubscribe(); void NotifyResubscribedUpToDate(); bool BCreateFromMsg( int nTypeID, const void *pvData, uint32 unSize ); bool BDestroyFromMsg( int nTypeID, const void *pvData, uint32 unSize ); bool BUpdateFromMsg( int nTypeID, const void *pvData, uint32 unSize ); #ifdef DBGFLAG_VALIDATE virtual void Validate( CValidator &validator, const char *pchName ); #endif protected: private: virtual CSharedObjectTypeCache *AllocateTypeCache( int nClassID ) const OVERRIDE { return new CGCClientSharedObjectTypeCache( nClassID, m_context ); } CGCClientSharedObjectTypeCache *GetTypeCacheByIndex( int nIndex ) { return (CGCClientSharedObjectTypeCache *)CSharedObjectCache::GetTypeCacheByIndex( nIndex ); } CGCClientSharedObjectContext m_context; bool m_bInitialized; bool m_bSubscribed; }; } // namespace GCSDK #endif //GCCLIENT_SHAREDOBJECTCACHE_H