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.
305 lines
10 KiB
305 lines
10 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Base class for objects that are kept in synch between client and server |
|
// |
|
//============================================================================= |
|
|
|
#ifndef SHAREDOBJECT_H |
|
#define SHAREDOBJECT_H |
|
#ifdef _WIN32 |
|
#pragma once |
|
#endif |
|
|
|
// ENABLE_SO_OVERWRITE_PARANOIA can be set to either 0 or 1. If enabled, it will add |
|
// extra fields to every CSharedObject instance to try and detect overwrites at the |
|
// cost of additional runtime memory. |
|
#define ENABLE_SO_OVERWRITE_PARANOIA 0 |
|
|
|
// ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA can be set to either 0 or 1. If enabled, it |
|
// will add extra fields to every CSharedObject instance to try and detect issues with |
|
// constructions/destruction (ie., double-deletes, etc.), including reference counting. |
|
#define ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA (defined( STAGING_ONLY )) |
|
|
|
#include "tier0/memdbgon.h" |
|
|
|
namespace GCSDK |
|
{ |
|
|
|
class CSQLAccess; |
|
class CSharedObject; |
|
typedef CSharedObject *(*SOCreationFunc_t)( ); |
|
class CSharedObjectCache; |
|
|
|
|
|
|
|
//---------------------------------------------------------------------------- |
|
// Purpose: Abstract base class for objects that are shared between the GC and |
|
// a gameserver/client. These can also be stored in the database. |
|
//---------------------------------------------------------------------------- |
|
class CSharedObject |
|
{ |
|
friend class CGCSharedObjectCache; |
|
friend class CSharedObjectCache; |
|
public: |
|
|
|
#ifdef GC |
|
virtual ~CSharedObject() |
|
{ |
|
#if ENABLE_SO_OVERWRITE_PARANOIA |
|
m_pThis = NULL; |
|
#endif // ENABLE_SO_OVERWRITE_PARANOIA |
|
|
|
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
AssertMsg2( m_nRefCount == 0, "Deleting shared object type %d with refcount %d", m_nSOTypeID, m_nRefCount ); |
|
|
|
m_nSOTypeID = -1; |
|
m_nRefCount = -1; |
|
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
} |
|
|
|
#if ENABLE_SO_OVERWRITE_PARANOIA |
|
CSharedObject *m_pThis; |
|
#endif // ENABLE_SO_OVERWRITE_PARANOIA |
|
|
|
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
int m_nRefCount; |
|
mutable int m_nSOTypeID; |
|
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
|
|
void Check() const |
|
{ |
|
#if ENABLE_SO_OVERWRITE_PARANOIA |
|
Assert( m_pThis == this ); |
|
#endif // ENABLE_SO_OVERWRITE_PARANOIA |
|
|
|
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
Assert( m_nRefCount >= 0 ); |
|
|
|
if ( m_nSOTypeID == kSharedObject_UnassignedType ) |
|
{ |
|
m_nSOTypeID = GetTypeID(); |
|
} |
|
|
|
Assert( m_nSOTypeID >= 0 ); |
|
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
} |
|
#else |
|
virtual ~CSharedObject() {} |
|
#endif |
|
|
|
virtual int GetTypeID() const = 0; |
|
virtual bool BParseFromMessage( const CUtlBuffer & buffer ) = 0; |
|
virtual bool BParseFromMessage( const std::string &buffer ) = 0; |
|
virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) = 0; |
|
virtual bool BIsKeyLess( const CSharedObject & soRHS ) const = 0; |
|
virtual void Copy( const CSharedObject & soRHS ) = 0; |
|
virtual void Dump() const = 0; |
|
virtual bool BShouldDeleteByCache() const { return true; } |
|
virtual CUtlString GetDebugString() const { return PchClassName( GetTypeID() ); }; |
|
|
|
bool BIsKeyEqual( const CSharedObject & soRHS ) const; |
|
|
|
static void RegisterFactory( int nTypeID, SOCreationFunc_t fnFactory, uint32 unFlags, const char *pchClassName ); |
|
static CSharedObject *Create( int nTypeID ); |
|
static uint32 GetTypeFlags( int nTypeID ); |
|
static const char *PchClassName( int nTypeID ); |
|
static const char *PchClassBuildCacheNodeName( int nTypeID ); |
|
static const char *PchClassCreateNodeName( int nTypeID ); |
|
static const char *PchClassUpdateNodeName( int nTypeID ); |
|
|
|
#ifdef GC |
|
virtual bool BIsNetworked() const { return true; } |
|
virtual bool BIsDatabaseBacked() const { return true; } |
|
virtual bool BYieldingAddToDatabase(); |
|
virtual bool BYieldingWriteToDatabase( const CUtlVector< int > &fields ); |
|
virtual bool BYieldingRemoveFromDatabase(); |
|
|
|
virtual bool BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess ) { return false; } |
|
virtual bool BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, const CUtlVector< int > &fields ) { return false; } |
|
virtual bool BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess ) { return false; } |
|
|
|
virtual bool BAddToMessage( CUtlBuffer & bufOutput ) const = 0; |
|
virtual bool BAddToMessage( std::string *pBuffer ) const = 0; |
|
virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const = 0; |
|
virtual bool BAddDestroyToMessage( std::string *pBuffer ) const = 0; |
|
|
|
virtual bool BParseFromMemcached( CUtlBuffer & buffer ) { return false; } |
|
virtual bool BAddToMemcached( CUtlBuffer & bufOutput ) const { return false; } |
|
|
|
bool BSendCreateToSteamID( const CSteamID & steamID, const CSteamID & ownerID, uint64 ulVersion ) const; |
|
bool BSendDestroyToSteamID( const CSteamID & steamID, const CSteamID & ownerID, uint64 ulVersion ) const; |
|
|
|
#ifdef DBGFLAG_VALIDATE |
|
virtual void Validate( CValidator &validator, const char *pchName ); |
|
static void ValidateStatics( CValidator & validator ); |
|
#endif |
|
|
|
protected: |
|
/* |
|
// Dirty bit modification. Do not call these directly on SharedObjects. Call them |
|
// on the cache that owns the object so they can be added/removed from the right lists. |
|
virtual void DirtyField( int nField ) = 0; |
|
virtual void MakeDatabaseClean() = 0; |
|
virtual void MakeNetworkClean() = 0; |
|
*/ |
|
#endif // GC |
|
|
|
#ifdef GC |
|
CSharedObject() |
|
{ |
|
#if ENABLE_SO_OVERWRITE_PARANOIA |
|
m_pThis = this; |
|
#endif // ENABLE_SO_OVERWRITE_PARANOIA |
|
|
|
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
m_nRefCount = 0; |
|
m_nSOTypeID = kSharedObject_UnassignedType; |
|
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
} |
|
#endif |
|
|
|
private: |
|
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
enum { kSharedObject_UnassignedType = -999 }; |
|
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA |
|
|
|
struct SharedObjectInfo_t |
|
{ |
|
SOCreationFunc_t m_pFactoryFunction; |
|
uint32 m_unFlags; |
|
const char *m_pchClassName; |
|
CUtlString m_sBuildCacheSubNodeName; |
|
CUtlString m_sUpdateNodeName; |
|
CUtlString m_sCreateNodeName; |
|
}; |
|
static CUtlMap<int, SharedObjectInfo_t> sm_mapFactories; |
|
|
|
public: |
|
static const CUtlMap<int, SharedObjectInfo_t> & GetFactories() { return sm_mapFactories; } |
|
}; |
|
|
|
typedef CUtlVectorFixedGrowable<CSharedObject *, 1> CSharedObjectVec; |
|
|
|
#ifdef GC |
|
|
|
//this class manages the stats for the shared objects, such as how many exist, how many have been sent, how many have been created, how many destroyed |
|
class CSharedObjectStats |
|
{ |
|
public: |
|
|
|
CSharedObjectStats(); |
|
|
|
//called to register a shared object class given a type ID and name. This will return false if |
|
//a type of this ID is already registered |
|
void RegisterSharedObjectType( int nTypeID, const char* pszTypeName ); |
|
|
|
//called to track when a shared object is created/destroyed to update the running total |
|
void TrackSharedObjectLifetime( int nTypeID, int32 nCount ); |
|
|
|
//called when a shared object is created to track its creation |
|
void TrackSharedObjectSendCreate( int nTypeID, uint32 nCount ); |
|
//called when a shared object is destroyed to track counts |
|
void TrackSharedObjectSendDestroy( int nTypeID, uint32 nCount ); |
|
|
|
//tracks a new subscription |
|
void TrackSubscription( int nTypeID, uint32 nFlags, uint32 nNumSubscriptions ); |
|
|
|
//called when a shared object is sent |
|
void TrackSharedObjectSend( int nTypeID, uint32 nNumClients, uint32 nMsgSize ); |
|
|
|
//reset the stats for all the caches |
|
void ResetStats(); |
|
|
|
//called to start/stop collecting stats |
|
void StartCollectingStats(); |
|
void StopCollectingStats(); |
|
bool IsCollectingStats() const { return m_bCollectingStats; } |
|
|
|
//called to report the currently collected stats |
|
void ReportCollectedStats() const; |
|
|
|
private: |
|
|
|
struct SOStats_t |
|
{ |
|
SOStats_t(); |
|
void ResetStats(); |
|
|
|
//the text name of this cache |
|
CUtlString m_sName; |
|
//the number of outstanding SO cache objects of this type. This is not cleared |
|
uint32 m_nNumActive; |
|
//the type ID of this stat |
|
int m_nTypeID; |
|
|
|
//the total number that have been created/destroyed. Active is difference between these |
|
uint32 m_nNumCreated; |
|
uint32 m_nNumDestroyed; |
|
//how many bytes we have sent (raw, and multiplexed to multiple subscribers) |
|
uint64 m_nRawBytesSent; |
|
uint64 m_nMultiplexedBytesSent; |
|
//the number of sends since our last clear |
|
uint32 m_nNumSends; |
|
//how many subscriptions we have gotten by various subscription types |
|
uint32 m_nNumSubOwner; |
|
uint32 m_nNumSubOtherUsers; |
|
uint32 m_nNumSubGameServer; |
|
}; |
|
|
|
//sort function that controls the order that the SO stats are presented in the report |
|
static bool SortSOStatsSent( const SOStats_t* pLhs, const SOStats_t* pRhs ); |
|
static bool SortSOStatsSubscribe( const SOStats_t* pLhs, const SOStats_t* pRhs ); |
|
|
|
//to compact the memory space for wide ranging type IDs, the list of stats has two components, an array that can be looked up |
|
//via the type ID which maps to a stat index (or invalid index if no mapping is registered) |
|
static const uint16 knInvalidIndex = ( uint16 )-1; |
|
CUtlVector< uint16 > m_vTypeToIndex; |
|
CUtlVector< SOStats_t > m_Stats; |
|
|
|
//are we currently collecting stats or not? |
|
bool m_bCollectingStats; |
|
//the time that we've been collecting |
|
CJobTime m_CollectTime; |
|
uint64 m_nMicroSElapsed; |
|
}; |
|
|
|
//global stat tracker |
|
extern CSharedObjectStats g_SharedObjectStats; |
|
|
|
#endif |
|
|
|
|
|
//---------------------------------------------------------------------------- |
|
// Purpose: Templatized function to use as a factory method for |
|
// CSharedObject subclasses |
|
//---------------------------------------------------------------------------- |
|
template<typename SharedObjectSubclass_t> |
|
CSharedObject *CreateSharedObjectSubclass() |
|
{ |
|
return new SharedObjectSubclass_t(); |
|
} |
|
|
|
// Version that always asserts and returns NULL, for SOs that should not be auto-created this way |
|
template<int nSharedObjectType> |
|
CSharedObject *CreateSharedObjectSubclassProhibited() |
|
{ |
|
AssertMsg( false, "Attempting to auto-create object of type %d which does not allow SO-based creation", nSharedObjectType ); |
|
return NULL; |
|
} |
|
|
|
#ifdef GC |
|
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass, flags ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, (flags), #derivedClass ) |
|
// GC only -- sharedobjects that cannot be auto-created by the SO code, and might not have a specific derived class that |
|
// publicly implements the interface |
|
#define REG_SHARED_OBJECT_SUBCLASS_BY_ID_NOCREATE( strName, nTypeID, flags ) \ |
|
GCSDK::CSharedObject::RegisterFactory( nTypeID, CreateSharedObjectSubclassProhibited<nTypeID>, (flags), strName ) |
|
#else |
|
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, 0, #derivedClass ) |
|
#endif |
|
|
|
} // namespace GCSDK |
|
|
|
|
|
#include "tier0/memdbgoff.h" |
|
|
|
#endif //SHAREDOBJECT_H
|
|
|