initial
This commit is contained in:
96
public/gcsdk/accountdetails.h
Normal file
96
public/gcsdk/accountdetails.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Holds the CAccountDetails class.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef ACCOUNTDETAILS_H
|
||||
#define ACCOUNTDETAILS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier1/thash.h"
|
||||
#include "tier1/utlhashmaplarge.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CAccountDetails
|
||||
{
|
||||
public:
|
||||
CAccountDetails();
|
||||
|
||||
void Init( KeyValues *pkvDetails );
|
||||
void Reset();
|
||||
bool BIsExpired() const;
|
||||
bool BIsValid() const { return m_bValid; }
|
||||
void SetCacheTime( int cacheTime );
|
||||
|
||||
const char *GetAccountName() const { return m_sAccountName.Get(); }
|
||||
const char *GetPersonaName() const { return m_sPersonaName.Get(); }
|
||||
bool BHasPublicProfile() const { return m_bPublicProfile; }
|
||||
bool BHasPublicInventory() const { return m_bPublicInventory; }
|
||||
bool BIsVacBanned() const { return m_bVacBanned; }
|
||||
bool BIsCyberCafe() const { return m_bCyberCafe; }
|
||||
bool BIsSchoolAccount() const { return m_bSchoolAccount; }
|
||||
bool BIsFreeTrialAccount() const { return m_bFreeTrialAccount; }
|
||||
bool BIsFreeTrialAccountOrDemo() const;
|
||||
bool BIsSubscribed() const { return m_bSubscribed; }
|
||||
bool BIsLowViolence() const { return m_bLowViolence; }
|
||||
bool BIsLimitedAccount() const { return m_bLimited; }
|
||||
bool BIsTrustedAccount() const { return m_bTrusted; }
|
||||
uint32 GetPackage() const { return m_unPackage; }
|
||||
RTime32 GetCacheTime() const { return m_rtimeCached; }
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
|
||||
private:
|
||||
RTime32 m_rtimeCached;
|
||||
CUtlConstString m_sAccountName;
|
||||
CUtlConstString m_sPersonaName;
|
||||
uint32 m_unPackage;
|
||||
bool
|
||||
m_bValid:1,
|
||||
m_bPublicProfile:1,
|
||||
m_bPublicInventory:1,
|
||||
m_bVacBanned:1,
|
||||
m_bCyberCafe:1,
|
||||
m_bSchoolAccount:1,
|
||||
m_bFreeTrialAccount:1,
|
||||
m_bSubscribed:1,
|
||||
m_bLowViolence:1,
|
||||
m_bLimited:1,
|
||||
m_bTrusted:1;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Manages requests for CAccountDetails objects
|
||||
//-----------------------------------------------------------------------------
|
||||
class CAccountDetailsManager
|
||||
{
|
||||
public:
|
||||
CAccountDetailsManager();
|
||||
CAccountDetails *YieldingGetAccountDetails( const CSteamID &steamID );
|
||||
|
||||
void MarkFrame() { m_hashAccountDetailsCache.StartFrameSchedule( true ); }
|
||||
bool BExpireAccountDetails( CLimitTimer &limitTimer );
|
||||
|
||||
void Dump() const;
|
||||
|
||||
private:
|
||||
friend class CGCJobSendGetAccountDetailsRequest;
|
||||
|
||||
bool BFindInLocalCache( const CSteamID &steamID, CAccountDetails **ppAccount );
|
||||
void WakeWaitingJobs( const CSteamID &steamID );
|
||||
|
||||
CTHash<CAccountDetails, uint32> m_hashAccountDetailsCache;
|
||||
CUtlHashMapLarge<CSteamID, CCopyableUtlVector<JobID_t> > m_mapQueuedRequests;
|
||||
};
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // ACCOUNTDETAILS_H
|
||||
40
public/gcsdk/enumutils.h
Normal file
40
public/gcsdk/enumutils.h
Normal file
@@ -0,0 +1,40 @@
|
||||
//========= Copyright ©, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: Makes enum-to-string and string-to-enum easier to declare
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCENUMUTILS_H
|
||||
#define GCENUMUTILS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
struct EnumString_s
|
||||
{
|
||||
int nValue;
|
||||
const char *pszString;
|
||||
};
|
||||
}
|
||||
|
||||
// starts defining a enum value string map
|
||||
#define ENUMSTRINGS_START( etype ) static const GCSDK::EnumString_s s_##etype[] = {
|
||||
|
||||
// ends defining a enum value string map. generates PchNameFromEnumName()
|
||||
#define ENUMSTRINGS_END( etype ) }; const char* PchNameFrom##etype( etype nValue ) \
|
||||
{ for( uint i=0; i<Q_ARRAYSIZE(s_##etype); i++ ) { if ( s_##etype[i].nValue == nValue ) return s_##etype[i].pszString; } \
|
||||
AssertMsg2( false, "Missing String for %s (%d)", #etype, nValue ); return "Unknown"; }
|
||||
|
||||
// ends defining a enum value string map. generates PchNameFromEnum() and EnumFromNam(). Invalid element must be first in array
|
||||
#define ENUMSTRINGS_REVERSE( etype, default ) }; const char* PchNameFrom##etype(etype nValue ) \
|
||||
{ for( uint i=0; i<Q_ARRAYSIZE(s_##etype); i++ ) { if ( s_##etype[i].nValue == nValue ) return s_##etype[i].pszString; } \
|
||||
AssertMsg2( false, "Missing String for %s (%d)", #etype, nValue ); return "Unknown"; }; \
|
||||
etype etype##FromName( const char *pchName ) \
|
||||
{ for( uint i=0; i<Q_ARRAYSIZE(s_##etype); i++ ) { if ( !Q_stricmp( s_##etype[i].pszString, pchName ) ) return (etype)(s_##etype[i].nValue); } \
|
||||
return default; }
|
||||
|
||||
|
||||
#endif // GCENUMUTILS_H
|
||||
84
public/gcsdk/gc_convar.h
Normal file
84
public/gcsdk/gc_convar.h
Normal file
@@ -0,0 +1,84 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Defines gc-specific convars that integrate their AppIDs so that
|
||||
// two GC's in the same shell don' overwrite each other
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCCONVAR_H
|
||||
#define GCCONVAR_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier1/convar.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: GC specifc ConVar
|
||||
//-----------------------------------------------------------------------------
|
||||
class GCConVar : public ConVar
|
||||
{
|
||||
public:
|
||||
GCConVar( const char *pName, const char *pDefaultValue, int flags = 0)
|
||||
: ConVar( pName, pDefaultValue, flags) {}
|
||||
GCConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString )
|
||||
: ConVar( pName, pDefaultValue, flags, pHelpString ) {}
|
||||
GCConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax )
|
||||
: ConVar( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax ) {}
|
||||
GCConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, FnChangeCallback_t callback )
|
||||
: ConVar( pName, pDefaultValue, flags, pHelpString, callback ) {}
|
||||
GCConVar( const char *pName, const char *pDefaultValue, int flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t callback )
|
||||
: ConVar( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax, callback ) {}
|
||||
|
||||
virtual const char *GetName( void ) const;
|
||||
const char *GetBaseName() const { GetName(); return m_pchBaseName; } // returns the name without the appID suffix
|
||||
|
||||
protected:
|
||||
mutable CUtlString m_strGCName;
|
||||
mutable const char *m_pchBaseName;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: GC specific ConCommand
|
||||
//-----------------------------------------------------------------------------
|
||||
class GCConCommand : public ConCommand
|
||||
{
|
||||
public:
|
||||
GCConCommand( const char *pName, FnCommandCallbackV1_t callback, const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 )
|
||||
: ConCommand( pName, callback, pHelpString, flags, completionFunc ) {}
|
||||
GCConCommand( const char *pName, FnCommandCallback_t callback, const char *pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0 )
|
||||
: ConCommand( pName, callback, pHelpString, flags, completionFunc ) {}
|
||||
GCConCommand( const char *pName, ICommandCallback *pCallback, const char *pHelpString = 0, int flags = 0, ICommandCompletionCallback *pCommandCompletionCallback = 0 )
|
||||
: ConCommand( pName, pCallback, pHelpString, flags, pCommandCompletionCallback ) {}
|
||||
|
||||
virtual const char *GetName( void ) const;
|
||||
const char *GetBaseName() const { GetName(); return m_pchBaseName; } // returns the name without the appID suffix
|
||||
|
||||
protected:
|
||||
mutable CUtlString m_strGCName;
|
||||
mutable const char *m_pchBaseName;
|
||||
};
|
||||
|
||||
#define GC_CON_COMMAND( name, description ) \
|
||||
static void name( const CCommand &args ); \
|
||||
static GCConCommand name##_command( #name, name, description ); \
|
||||
static void name( const CCommand &args )
|
||||
|
||||
#define AUTO_CONFIRM_CON_COMMAND() \
|
||||
{ \
|
||||
static RTime32 rtimeLastRan = 0; \
|
||||
if ( CRTime::RTime32TimeCur() - rtimeLastRan > 3 ) \
|
||||
{ \
|
||||
rtimeLastRan = CRTime::RTime32TimeCur(); \
|
||||
EmitInfo( SPEW_CONSOLE, SPEW_ALWAYS, LOG_ALWAYS, "Auto-confirm: Please repeat command within 3 seconds to confirm.\n" ); \
|
||||
return; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
rtimeLastRan = 0; \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
376
public/gcsdk/gc_sharedobjectcache.h
Normal file
376
public/gcsdk/gc_sharedobjectcache.h
Normal file
@@ -0,0 +1,376 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Additional shared object cache functionality for the GC
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GC_SHAREDOBJECTCACHE_H
|
||||
#define GC_SHAREDOBJECTCACHE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "sharedobjectcache.h"
|
||||
|
||||
class CMsgSOCacheSubscribed_SubscribedType;
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CCachedSubscriptionMessage;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: The part of a shared object cache that handles all objects of a
|
||||
// single type.
|
||||
//----------------------------------------------------------------------------
|
||||
class CSharedObjectContext
|
||||
{
|
||||
public:
|
||||
CSharedObjectContext( const CSteamID & steamIDOwner );
|
||||
|
||||
bool BAddSubscriber( const CSteamID & steamID );
|
||||
bool BRemoveSubscriber( const CSteamID & steamID );
|
||||
void RemoveAllSubscribers();
|
||||
bool BIsSubscribed( const CSteamID & steamID ) { return m_vecSubscribers.Find( steamID ) != m_vecSubscribers.InvalidIndex(); }
|
||||
|
||||
const CUtlVector< CSteamID > & GetSubscribers() const { return m_vecSubscribers; }
|
||||
const CSteamID & GetOwner() const { return m_steamIDOwner; }
|
||||
private:
|
||||
CUtlVector<CSteamID> m_vecSubscribers;
|
||||
CUtlVector<int> m_vecSubRefCount;
|
||||
CSteamID m_steamIDOwner;
|
||||
};
|
||||
|
||||
class CGCSharedObjectTypeCache;
|
||||
|
||||
enum ESOTypeFlags
|
||||
{
|
||||
k_SOFlag_SendToNobody = 0,
|
||||
k_ESOFlag_SendToOwningUser = 1 << 0, // will go only to the owner of the cache (if that owner is a user)
|
||||
k_ESOFlag_SendToOtherUsers = 1 << 1, // will go only to users who are not the owner of the cache
|
||||
k_SOFlag_SendToGameservers = 1 << 2, // will go only to gameservers, regardless of whether they're the owner
|
||||
k_SOFlag_LastFlag = k_SOFlag_SendToGameservers,
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Filter object used to determine whether a type cache's objects should
|
||||
// should be sent to subscribers and whether each object should be sent
|
||||
//----------------------------------------------------------------------------
|
||||
class CISubscriberMessageFilter
|
||||
{
|
||||
public:
|
||||
virtual bool BShouldSendAnyObjectsInCache( CGCSharedObjectTypeCache *pTypeCache, uint32 unFlags ) const = 0;
|
||||
virtual bool BShouldSendObject( CSharedObject *pSharedObject, uint32 unFlags ) const = 0;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: The part of a shared object cache that handles all objects of a
|
||||
// single type.
|
||||
//----------------------------------------------------------------------------
|
||||
class CGCSharedObjectTypeCache : public CSharedObjectTypeCache
|
||||
{
|
||||
public:
|
||||
|
||||
typedef CSharedObjectTypeCache Base;
|
||||
|
||||
CGCSharedObjectTypeCache( int nTypeID, const CSharedObjectContext & context );
|
||||
virtual ~CGCSharedObjectTypeCache();
|
||||
|
||||
virtual bool AddObject( CSharedObject *pObject );
|
||||
virtual CSharedObject *RemoveObject( const CSharedObject & soIndex );
|
||||
|
||||
void BuildCacheSubscribedMsg( CMsgSOCacheSubscribed_SubscribedType *pMsgType, uint32 unFlags, const CISubscriberMessageFilter &filter );
|
||||
virtual void EnsureCapacity( uint32 nItems );
|
||||
|
||||
static int GetCachedObjectCount( int nTypeID );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
|
||||
private:
|
||||
static int UpdateCachedObjectCount( int nTypeID, int nDelta );
|
||||
|
||||
const CSharedObjectContext & m_context;
|
||||
|
||||
static CUtlMap<int, int> sm_mapCachedObjectCounts;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// 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 CGCSharedObjectCache : public CSharedObjectCache
|
||||
{
|
||||
public:
|
||||
CGCSharedObjectCache( const CSteamID & steamIDOwner = CSteamID() );
|
||||
virtual ~CGCSharedObjectCache();
|
||||
|
||||
const CSteamID & GetOwner() const { return m_context.GetOwner(); }
|
||||
const CUtlVector< CSteamID > & GetSubscribers() const { return m_context.GetSubscribers(); }
|
||||
|
||||
CGCSharedObjectTypeCache *GetTypeCache( int nClassID, bool bCreateIfMissing = false ) { return (CGCSharedObjectTypeCache *)GetBaseTypeCache( nClassID, bCreateIfMissing ); }
|
||||
CGCSharedObjectTypeCache *GetTypeCache( int nClassID ) const { return (CGCSharedObjectTypeCache *)const_cast<CGCSharedObjectCache*>(this)->GetBaseTypeCache( nClassID, false ); }
|
||||
virtual CSharedObjectTypeCache *CreateTypeCache( int nClassID ) const { return new CGCSharedObjectTypeCache( nClassID, m_context ); }
|
||||
|
||||
// returns various singleton objects
|
||||
template< typename SOClass_t >
|
||||
SOClass_t *GetSingleton() const
|
||||
{
|
||||
GCSDK::CGCSharedObjectTypeCache *pTypeCache = GetTypeCache( SOClass_t::k_nTypeID );
|
||||
if ( pTypeCache && pTypeCache->GetCount() == 1 )
|
||||
{
|
||||
return (SOClass_t *)pTypeCache->GetObject( 0 );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual uint32 CalcSendFlags( const CSteamID &steamID ) const;
|
||||
virtual const CISubscriberMessageFilter &GetSubscriberMessageFilter();
|
||||
virtual void AddObject( CSharedObject *pSharedObject );
|
||||
bool BDestroyObject( const CSharedObject & soIndex, bool bRemoveFromDatabase );
|
||||
virtual CSharedObject *RemoveObject( const CSharedObject & soIndex );
|
||||
|
||||
template< typename SOClass_t >
|
||||
bool BYieldingLoadSchObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & schDefaults );
|
||||
|
||||
template< typename SOClass_t >
|
||||
bool BYieldingLoadSchSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & schDefaults );
|
||||
|
||||
template< typename SOClass_t, typename SchClass_t >
|
||||
bool BYieldingLoadProtoBufObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead );
|
||||
|
||||
template< typename SOClass_t, typename SchClass_t >
|
||||
bool BYieldingLoadProtoBufSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SchClass_t & schDefaults );
|
||||
|
||||
// @todo temporary for trading and item subscriptions (to be removed once we get cross-game trading)
|
||||
virtual void SetTradingPartner( const CSteamID &steamID );
|
||||
const CSteamID &GetTradingPartner() const { return m_steamIDTradingPartner; }
|
||||
|
||||
void AddSubscriber( const CSteamID & steamID );
|
||||
void RemoveSubscriber( const CSteamID & steamID );
|
||||
void RemoveAllSubscribers();
|
||||
void SendSubscriberMessage( const CSteamID & steamID );
|
||||
bool BIsSubscribed( const CSteamID & steamID ) { return m_context.BIsSubscribed( steamID ); }
|
||||
void ClearCachedSubscriptionMessage();
|
||||
|
||||
bool BIsDatabaseDirty() const { return m_databaseDirtyList.NumDirtyObjects() > 0; }
|
||||
|
||||
// This will mark the field as dirty for both network and database
|
||||
void DirtyObjectField( CSharedObject *pObj, int nFieldIndex );
|
||||
|
||||
// Marks only dirty for network
|
||||
void DirtyNetworkObjectField( CSharedObject *pObj, int nFieldIndex );
|
||||
|
||||
// Mark dirty for database
|
||||
void DirtyDatabaseObjectField( CSharedObject *pObj, int nFieldIndex );
|
||||
|
||||
void SendNetworkUpdates( CSharedObject *pObj );
|
||||
bool BYieldingAddWriteToTransaction( CSharedObject *pObj, CSQLAccess & sqlAccess );
|
||||
void SendAllNetworkUpdates();
|
||||
void YieldingWriteToDatabase( CSharedObject *pObj );
|
||||
uint32 AddAllWritesToTransaction( CSQLAccess & sqlAccess );
|
||||
void CleanAllWrites( );
|
||||
|
||||
void SetInWriteback( bool bInWriteback );
|
||||
bool GetInWriteback() const { return m_bInWriteback; }
|
||||
RTime32 GetWritebackTime() const { return m_unWritebackTime; }
|
||||
|
||||
void SetLRUHandle( uint32 unLRUHandle ) { m_unLRUHandle = unLRUHandle; }
|
||||
uint32 GetLRUHandle() const { return m_unLRUHandle; }
|
||||
|
||||
void Dump() const;
|
||||
void DumpDirtyObjects() const;
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsObjectCached( CSharedObject *pObj, int nTypeID );
|
||||
bool IsObjectDirty( CSharedObject *pObj );
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void MarkDirty();
|
||||
virtual bool BShouldSendToAnyClients( uint32 unFlags ) const;
|
||||
CCachedSubscriptionMessage *BuildSubscriberMessage( uint32 unFlags );
|
||||
|
||||
CSteamID m_steamIDTradingPartner;
|
||||
|
||||
protected:
|
||||
void SendNetworkUpdateInternal( CSharedObject * pObj, const CUtlVector< int > &dirtyFields );
|
||||
void SendUnsubscribeMessage( const CSteamID & steamID );
|
||||
|
||||
CSharedObjectContext m_context;
|
||||
CSharedObjectDirtyList m_networkDirtyList;
|
||||
CSharedObjectDirtyList m_databaseDirtyList;
|
||||
bool m_bInWriteback;
|
||||
RTime32 m_unWritebackTime;
|
||||
uint32 m_unLRUHandle;
|
||||
const IProtoBufMsg *m_pCacheToUsersSubscriptionMsg;
|
||||
uint32 m_unCachedSubscriptionMsgFlags;
|
||||
CCachedSubscriptionMessage *m_pCachedSubscriptionMsg;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Loads a list of CSchemaSharedObjects from a result list from a
|
||||
// query.
|
||||
// Inputs: pResultSet - The result set from the SQL query
|
||||
// schDefaults - A schema object that defines the values to set in
|
||||
// the new objects for fields that were not read in the query.
|
||||
// Typically this will be whatever fields were in the WHERE
|
||||
// clause of the query.
|
||||
// csRead - A columnSet defining the fields that were read in the query.
|
||||
//----------------------------------------------------------------------------
|
||||
template< typename SOClass_t >
|
||||
bool CGCSharedObjectCache::BYieldingLoadSchObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & objDefaults )
|
||||
{
|
||||
if ( NULL == pResultSet )
|
||||
return false;
|
||||
|
||||
CGCSharedObjectTypeCache *pTypeCache = GetTypeCache( SOClass_t::k_nTypeID, true );
|
||||
pTypeCache->EnsureCapacity( pResultSet->GetRowCount() );
|
||||
for( CSQLRecord record( 0, pResultSet ); record.IsValid(); record.NextRow() )
|
||||
{
|
||||
SOClass_t *pObj = new SOClass_t();
|
||||
pObj->Obj() = objDefaults.Obj();
|
||||
record.BWriteToRecord( &pObj->Obj(), csRead );
|
||||
pTypeCache->AddObject( pObj );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Loads a single object of a type. If the object is not available,
|
||||
// a new object will be created at default values
|
||||
// Inputs: pResultSet - The result set from the SQL query
|
||||
// schDefaults - A schema object that defines the values to set in
|
||||
// the new objects for fields that were not read in the query.
|
||||
// Typically this will be whatever fields were in the WHERE
|
||||
// clause of the query.
|
||||
// csRead - A columnSet defining the fields that were read in the query.
|
||||
//----------------------------------------------------------------------------
|
||||
template< typename SOClass_t >
|
||||
bool CGCSharedObjectCache::BYieldingLoadSchSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SOClass_t & objDefaults )
|
||||
{
|
||||
if ( NULL == pResultSet )
|
||||
return false;
|
||||
|
||||
if ( pResultSet->GetRowCount() > 1 )
|
||||
{
|
||||
EmitError( SPEW_SHAREDOBJ, "Multiple rows passed to BYieldingLoadSchSingleton() on type %d\n", objDefaults.GetTypeID() );
|
||||
return false;
|
||||
}
|
||||
else if ( pResultSet->GetRowCount() == 1 )
|
||||
{
|
||||
return BYieldingLoadSchObjects<SOClass_t>( pResultSet, csRead, objDefaults );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create it if there wasn't one
|
||||
SOClass_t *pSchObj = new SOClass_t();
|
||||
pSchObj->Obj() = objDefaults.Obj();
|
||||
if( !pSchObj->BYieldingAddToDatabase() )
|
||||
{
|
||||
EmitError( SPEW_SHAREDOBJ, "Unable to add singleton type %d for %s\n", pSchObj->GetTypeID(), GetOwner().Render() );
|
||||
return false;
|
||||
}
|
||||
AddObject( pSchObj );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Loads a list of CProtoBufSharedObjects from a result list from a
|
||||
// query.
|
||||
// Inputs: pResultSet - The result set from the SQL query
|
||||
// schDefaults - A schema object that defines the values to set in
|
||||
// the new objects for fields that were not read in the query.
|
||||
// Typically this will be whatever fields were in the WHERE
|
||||
// clause of the query.
|
||||
// csRead - A columnSet defining the fields that were read in the query.
|
||||
//----------------------------------------------------------------------------
|
||||
template< typename SOClass_t, typename SchClass_t >
|
||||
bool CGCSharedObjectCache::BYieldingLoadProtoBufObjects( IGCSQLResultSet *pResultSet, const CColumnSet & csRead )
|
||||
{
|
||||
if ( NULL == pResultSet )
|
||||
return false;
|
||||
|
||||
CGCSharedObjectTypeCache *pTypeCache = GetTypeCache( SOClass_t::k_nTypeID, true );
|
||||
pTypeCache->EnsureCapacity( pResultSet->GetRowCount() );
|
||||
for( CSQLRecord record( 0, pResultSet ); record.IsValid(); record.NextRow() )
|
||||
{
|
||||
SchClass_t schRecord;
|
||||
record.BWriteToRecord( &schRecord, csRead );
|
||||
|
||||
SOClass_t *pObj = new SOClass_t();
|
||||
pObj->ReadFromRecord( schRecord );
|
||||
pTypeCache->AddObject( pObj );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Loads a single object of a type. If the object is not available,
|
||||
// a new object will be created at default values
|
||||
// Inputs: pResultSet - The result set from the SQL query
|
||||
// schDefaults - A schema object that defines the values to set in
|
||||
// the new objects for fields that were not read in the query.
|
||||
// Typically this will be whatever fields were in the WHERE
|
||||
// clause of the query.
|
||||
// csRead - A columnSet defining the fields that were read in the query.
|
||||
//----------------------------------------------------------------------------
|
||||
template< typename SOClass_t, typename SchClass_t >
|
||||
bool CGCSharedObjectCache::BYieldingLoadProtoBufSingleton( IGCSQLResultSet *pResultSet, const CColumnSet & csRead, const SchClass_t & schDefaults )
|
||||
{
|
||||
if ( NULL == pResultSet )
|
||||
return false;
|
||||
|
||||
if ( pResultSet->GetRowCount() > 1 )
|
||||
{
|
||||
EmitError( SPEW_SHAREDOBJ, "Multiple rows passed to BYieldingLoadProtoBufSingleton() on type %d\n", SOClass_t::k_nTypeID );
|
||||
return false;
|
||||
}
|
||||
|
||||
// load the duel summary
|
||||
SchClass_t schRead;
|
||||
CSQLRecord record( 0, pResultSet );
|
||||
if( record.IsValid() )
|
||||
{
|
||||
record.BWriteToRecord( &schRead, csRead );
|
||||
}
|
||||
else
|
||||
{
|
||||
CSQLAccess sqlAccess;
|
||||
if( !sqlAccess.BYieldingInsertRecord( const_cast<SchClass_t *>( &schDefaults ) ) )
|
||||
return false;
|
||||
schRead = schDefaults;
|
||||
}
|
||||
|
||||
SOClass_t *pSharedObject = new SOClass_t();
|
||||
pSharedObject->ReadFromRecord( schRead );
|
||||
AddObject( pSharedObject );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif //GC_SHAREDOBJECTCACHE_H
|
||||
303
public/gcsdk/gcbase.h
Normal file
303
public/gcsdk/gcbase.h
Normal file
@@ -0,0 +1,303 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCBASE_H
|
||||
#define GCBASE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gamecoordinator/igamecoordinator.h"
|
||||
#include "gamecoordinator/igamecoordinatorhost.h"
|
||||
#include "tier1/utlallocation.h"
|
||||
#include "gcmsg.h"
|
||||
#include "jobmgr.h"
|
||||
#include "tier1/thash.h"
|
||||
#include "tier1/utlsortvector.h"
|
||||
#include "http.h"
|
||||
#include "language.h"
|
||||
#include "accountdetails.h"
|
||||
|
||||
class CGCMsgGetSystemStatsResponse;
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CGCSession;
|
||||
class CGCUserSession;
|
||||
class CGCGSSession;
|
||||
class CGCSharedObjectCache;
|
||||
class CSharedObject;
|
||||
class CAccountDetails;
|
||||
|
||||
struct PackageLicense_t
|
||||
{
|
||||
uint32 m_unPackageID;
|
||||
RTime32 m_rtimeCreated;
|
||||
};
|
||||
|
||||
|
||||
class CGCBase : public IGameCoordinator
|
||||
{
|
||||
public:
|
||||
CGCBase( );
|
||||
virtual ~CGCBase();
|
||||
|
||||
// Hooks to extend the base behaviors of the IGameCoordinator interface
|
||||
virtual bool OnInit() { return true; }
|
||||
virtual bool OnMainLoopOncePerFrame( CLimitTimer &limitTimer ) { return false; }
|
||||
virtual bool OnMainLoopUntilFrameCompletion( CLimitTimer &limitTimer ) { return false; }
|
||||
virtual void OnUninit() {}
|
||||
// returns true if this function handled the message
|
||||
virtual bool OnMessageFromClient( const CSteamID & senderID, uint32 unMsgType, void *pubData, uint32 cubData ) { return false; }
|
||||
virtual void OnValidate( CValidator &validator, const char *pchName ) {}
|
||||
virtual const char *LocalizeToken( const char *pchToken, ELanguage eLanguage, bool bReturnTokenIfNotFound = true ) { return ""; }
|
||||
|
||||
// Life cycle management functions
|
||||
|
||||
// Called to do any yielding initialization work before reporting as fully operational
|
||||
virtual bool BYieldingFinishStartup() = 0;
|
||||
|
||||
// Called to do any yielding work immediately after becoming full operational
|
||||
virtual bool BYieldingPostStartup() = 0;
|
||||
|
||||
// Call to report that we're fully operational
|
||||
void SetStartupComplete( bool bSuccess );
|
||||
|
||||
// Called to do any yielding work before reporting as ready to shutdown
|
||||
virtual void YieldingGracefulShutdown() = 0;
|
||||
|
||||
|
||||
virtual CGCUserSession *CreateUserSession( const CSteamID & steamID, CGCSharedObjectCache *pSOCache ) const;
|
||||
virtual CGCGSSession *CreateGSSession( const CSteamID & steamID, CGCSharedObjectCache *pSOCache, uint32 unServerAddr, uint16 usServerPort ) const;
|
||||
virtual void YieldingSessionStartPlaying( CGCUserSession *pSession ) {}
|
||||
virtual void YieldingSessionStopPlaying( CGCUserSession *pSession ) {}
|
||||
virtual void YieldingSessionStartServer( CGCGSSession *pSession ) {}
|
||||
virtual void YieldingSessionStopServer( CGCGSSession *pSession ) {}
|
||||
virtual void YieldingSOCacheLoaded( CGCSharedObjectCache *pSOCache );
|
||||
|
||||
virtual void YieldingPreTestSetup() {}
|
||||
|
||||
// cache management
|
||||
CGCSharedObjectCache *YieldingGetLockedSOCache( const CSteamID &steamID );
|
||||
CGCSharedObjectCache *YieldingFindOrLoadSOCache( const CSteamID &steamID );
|
||||
CGCSharedObjectCache *FindSOCache( const CSteamID & steamID ); // non-yielding, but may return NULL if the cache exists but is not loaded
|
||||
bool UnloadUnusedCaches( uint32 unMaxCacheCount, CLimitTimer *pLimitTimer = NULL );
|
||||
void YieldingReloadCache( CGCSharedObjectCache *pSOCache );
|
||||
virtual CGCSharedObjectCache *CreateSOCache( const CSteamID &steamID );
|
||||
|
||||
CGCUserSession *YieldingGetLockedUserSession( const CSteamID & steamID );
|
||||
CGCUserSession *FindUserSession( const CSteamID & steamID ) const;
|
||||
|
||||
CGCGSSession *YieldingGetLockedGSSession( const CSteamID & steamID );
|
||||
CGCGSSession *YieldingFindOrCreateGSSession( const CSteamID & steamID, uint32 unServerAddr, uint16 usServerPort, const uint8 *pubVarData, uint32 cubVarData );
|
||||
CGCGSSession *FindGSSession( const CSteamID & steamID ) const;
|
||||
|
||||
CGCSession *YieldingRequestSession( const CSteamID & steamID );
|
||||
bool BYieldingIsOnline( const CSteamID & steamID );
|
||||
bool BYieldingSendHTTPRequest( CHTTPRequest *pRequest, CHTTPResponse *pResponse );
|
||||
bool BYieldingSendHTTPRequest( CHTTPRequest *pRequest, KeyValues *pkvResponse );
|
||||
bool BSendWebApiRegistration();
|
||||
|
||||
int GetSOCacheCount() const;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsSOCached( CSharedObject *pObj, int nTypeID );
|
||||
#endif
|
||||
|
||||
int GetUserSessionCount() const;
|
||||
int GetGSSessionCount() const;
|
||||
|
||||
void SetIsShuttingDown( bool bIsShuttingDown ) { m_bIsShuttingDown = true; }
|
||||
bool GetIsShuttingDown() const { return m_bIsShuttingDown; }
|
||||
|
||||
// Iterate over sessions
|
||||
// WARNING: Don't yield between GetFirst*/GetNext*. Use caution when using
|
||||
// these any time you might have tens of thousands of sessions to iterate through.
|
||||
CGCUserSession **GetFirstUserSession() { return m_hashUserSessions.PvRecordFirst(); }
|
||||
CGCUserSession **GetNextUserSession( CGCUserSession **pUserSession ) { return m_hashUserSessions.PvRecordNext( pUserSession ); }
|
||||
CGCGSSession **GetFirstGSSession() { return m_hashGSSessions.PvRecordFirst(); }
|
||||
CGCGSSession **GetNextGSSession( CGCGSSession **pGSSession ) { return m_hashGSSessions.PvRecordNext( pGSSession ); }
|
||||
|
||||
typedef CUtlSortVector< CGCGSSession *, CUtlSortVectorDefaultLess< CGCGSSession * >, CCopyableUtlVector< CGCGSSession * > > CGCGSSessionSortedVector_t;
|
||||
CGCGSSessionSortedVector_t const *GetGSSessionVectorByType( int serverType );
|
||||
|
||||
// This can cause the server pointer to change between lists, call it here
|
||||
void SetGSServerType( CGCGSSession *pGSSession, int serverType );
|
||||
|
||||
AppId_t GetAppID() const { return m_unAppID; }
|
||||
bool BIsStartupComplete() const { return m_bStartupComplete; }
|
||||
CJobMgr &GetJobMgr() { return m_JobMgr; }
|
||||
|
||||
CSteamID YieldingGuessSteamIDFromInput( const char *pchInput );
|
||||
bool BYieldingRecordSupportAction( const CSteamID & actorID, const CSteamID & targetID, const char *pchData, const char *pchNote );
|
||||
void PostAlert( EAlertType eAlertType, bool bIsCritical, const char *pchAlertText, const CUtlVector< CUtlString > *pvecExtendedInfo = NULL, bool bAlsoSpew = true );
|
||||
CAccountDetails *YieldingGetAccountDetails( const CSteamID & steamID );
|
||||
bool BYieldingGetAccountLicenses( const CSteamID & steamID, CUtlVector< PackageLicense_t > & vecPackages );
|
||||
bool BYieldingLookupAccount( EAccountFindType eFindType, const char *pchInput, CUtlVector< CSteamID > *prSteamIDs );
|
||||
bool BYieldingAddFreeLicense( const CSteamID & steamID, uint32 unPackageID, uint32 unIPPublic = 0, const char *pchStoreCountryCode = NULL );
|
||||
|
||||
bool BSendGCMsgToClient( const CSteamID & steamIDTarget, const CGCMsgBase& msg );
|
||||
bool BSendGCMsgToClient( const CSteamID & steamIDTarget, const IProtoBufMsg& msg );
|
||||
bool BSendSystemMessage( const CGCMsgBase& msg );
|
||||
bool BSendSystemMessage( const IProtoBufMsg& msg );
|
||||
|
||||
bool BSendGCInterMessage( AppId_t unAppID, const IProtoBufMsg &msg );
|
||||
bool BReplyToMessage( CGCMsgBase &msgOut, const CGCMsgBase &msgIn );
|
||||
bool BReplyToMessage( IProtoBufMsg &msgOut, const IProtoBufMsg &msgIn );
|
||||
|
||||
const char *GetPath() const { return m_sPath; }
|
||||
virtual const char *GetSteamAPIKey();
|
||||
|
||||
void QueueStartPlaying( const CSteamID & steamID, const CSteamID & gsSteamID, uint32 unServerAddr, uint16 usServerPort, const uint8 *pubVarData, uint32 cubVarData );
|
||||
void YieldingExecuteNextStartPlaying();
|
||||
bool BIsInLogonSurge() const;
|
||||
|
||||
void YieldingStopPlaying( const CSteamID & steamID );
|
||||
void YieldingStartGameserver( const CSteamID & steamID, uint32 unServerAddr, uint16 usServerPort, const uint8 *pubVarData, uint32 cubVarData );
|
||||
void YieldingStopGameserver( const CSteamID & steamID );
|
||||
bool BIsSOCacheBeingLoaded( const CSteamID & steamID ) const { return m_rbtreeSOCachesBeingLoaded.Find( steamID ) != m_rbtreeSOCachesBeingLoaded.InvalidIndex(); }
|
||||
|
||||
bool BYieldingLockSteamID( const CSteamID &steamID );
|
||||
bool BYieldingLockSteamIDPair( const CSteamID &steamIDA, const CSteamID &steamIDB );
|
||||
bool BLockSteamIDImmediate( const CSteamID &steamID );
|
||||
void UnlockSteamID( const CSteamID &steamID );
|
||||
bool IsSteamIDLocked( const CSteamID &steamID );
|
||||
bool IsSteamIDLockedByJob( const CSteamID &steamID, const CJob *pJob );
|
||||
bool IsSteamIDUnlockedOrLockedByCurJob( const CSteamID &steamID );
|
||||
CJob *PJobHoldingLock( const CSteamID &steamID );
|
||||
|
||||
const CLock *FindLock( const CSteamID &steamID );
|
||||
void DumpLocks( bool bFull, int nMax = 10 );
|
||||
void DumpJobs( int nMax ) const;
|
||||
virtual void Dump() const;
|
||||
void VerifySOCacheLRU();
|
||||
|
||||
void SetProfilingEnabled( bool bEnabled );
|
||||
void SetDumpVprofImbalances( bool bEnabled );
|
||||
bool GetVprofImbalances();
|
||||
|
||||
bool YieldingWritebackDirtyCaches( uint32 unSecondToDelayWrite );
|
||||
void AddCacheToWritebackQueue( CGCSharedObjectCache *pSOCache );
|
||||
|
||||
bool BYieldingRetrieveCacheVersion( CGCSharedObjectCache *pSOCache );
|
||||
void AddCacheToVersionChangedList( CGCSharedObjectCache *pSOCache );
|
||||
|
||||
const char *GetCDNURL() const;
|
||||
|
||||
struct GCMemcachedBuffer_t
|
||||
{
|
||||
const void *m_pubData;
|
||||
int m_cubData;
|
||||
};
|
||||
|
||||
struct GCMemcachedGetResult_t
|
||||
{
|
||||
bool m_bKeyFound; // true if the key was found
|
||||
CUtlAllocation m_bufValue; // the value of the key
|
||||
};
|
||||
|
||||
// Memcached access
|
||||
bool BMemcachedSet( const char *pKey, const ::google::protobuf::Message &protoBufObj );
|
||||
bool BMemcachedDelete( const char *pKey );
|
||||
bool BYieldingMemcachedGet( const char *pKey, ::google::protobuf::Message &protoBufObj );
|
||||
bool BMemcachedSet( const CUtlString &strKey, const CUtlBuffer &buf );
|
||||
bool BMemcachedSet( const CUtlVector<CUtlString> &vecKeys, const CUtlVector<GCMemcachedBuffer_t> &vecValues );
|
||||
bool BMemcachedDelete( const CUtlString &strKey );
|
||||
bool BMemcachedDelete( const CUtlVector<CUtlString> &vecKeys );
|
||||
bool BYieldingMemcachedGet( const CUtlString &strKey, GCMemcachedGetResult_t &result );
|
||||
bool BYieldingMemcachedGet( const CUtlVector<CUtlString> &vecKeys, CUtlVector<GCMemcachedGetResult_t> &vecResults );
|
||||
|
||||
// IP location
|
||||
bool BYieldingGetIPLocations( CUtlVector<uint32> &vecIPs, CUtlVector<CIPLocationInfo> &infos );
|
||||
|
||||
// Stats
|
||||
virtual void SystemStats_Update( CGCMsgGetSystemStatsResponse &msgStats );
|
||||
|
||||
protected:
|
||||
virtual bool BYieldingLoadSOCache( CGCSharedObjectCache *pSOCache );
|
||||
void RemoveSOCache( const CSteamID & steamID );
|
||||
|
||||
void AddCacheToLRU( CGCSharedObjectCache * pSOCache );
|
||||
void RemoveCacheFromLRU( CGCSharedObjectCache * pSOCache );
|
||||
|
||||
void SetStartupComplete() { m_bStartupComplete = true; }
|
||||
private:
|
||||
|
||||
static void AssertCallbackFunc( const char *pchFile, int nLine, const char *pchMessage );
|
||||
void UpdateSOCacheVersions();
|
||||
|
||||
// this is called from ExecuteNextStartPlaying and nowhere else
|
||||
void YieldingStartPlaying( const CSteamID & steamID, const CSteamID & gsSteamID, uint32 unServerAddr, uint16 usServerPort, CUtlBuffer *pVarData );
|
||||
|
||||
// Base behaviors of the IGameCoordinator interface. These are not overridable.
|
||||
// They each call the On* version below, which is overridable by subclasses
|
||||
virtual bool BInit( AppId_t unAppID, const char *pchAppPath, IGameCoordinatorHost *pHost );
|
||||
virtual bool BMainLoopOncePerFrame( uint64 ulLimitMicroseconds );
|
||||
virtual bool BMainLoopUntilFrameCompletion( uint64 ulLimitMicroseconds );
|
||||
virtual void Shutdown();
|
||||
virtual void Uninit();
|
||||
virtual void MessageFromClient( const CSteamID & senderID, uint32 unMsgType, void *pubData, uint32 cubData );
|
||||
virtual void Validate( CValidator &validator, const char *pchName );
|
||||
virtual void SQLResults( GID_t gidContextID );
|
||||
|
||||
// profiling
|
||||
bool m_bStartProfiling;
|
||||
bool m_bStopProfiling;
|
||||
bool m_bDumpVprofImbalances;
|
||||
|
||||
// local job handling
|
||||
CJobMgr m_JobMgr;
|
||||
CGCWGJobMgr m_wgJobMgr;
|
||||
|
||||
// session tracking
|
||||
CTHash<CGCUserSession *, uint64> m_hashUserSessions;
|
||||
CTHash<CGCGSSession *, uint64> m_hashGSSessions;
|
||||
typedef CUtlMap< int, CGCGSSessionSortedVector_t > CGCGSSessionServerTypeMap_t;
|
||||
CGCGSSessionServerTypeMap_t m_GSSessionsByTypeMap;
|
||||
CUtlMap< uint64, CGCGSSession*, int > m_mapGSSessionsByNetAddress;
|
||||
|
||||
// Shared object caches
|
||||
CUtlMap<CSteamID, CGCSharedObjectCache *, int> m_mapSOCache;
|
||||
CUtlVector< CGCSharedObjectCache * >m_vecCacheWritebacks;
|
||||
CUtlLinkedList< CSteamID, uint32> m_listCachesToUnload;
|
||||
CUtlRBTree<CSteamID, int > m_rbtreeSOCachesBeingLoaded;
|
||||
CUtlRBTree<CSteamID, int > m_rbtreeSOCachesWithDirtyVersions;
|
||||
|
||||
// steamID locks
|
||||
CTHash<CLock, CSteamID> m_hashSteamIDLocks;
|
||||
|
||||
// Account Details
|
||||
CAccountDetailsManager m_AccountDetailsManager;
|
||||
|
||||
// State
|
||||
AppId_t m_unAppID;
|
||||
CUtlString m_sPath;
|
||||
IGameCoordinatorHost *m_pHost;
|
||||
bool m_bStartupComplete;
|
||||
bool m_bIsShuttingDown;
|
||||
|
||||
struct StartPlayingWork_t
|
||||
{
|
||||
CSteamID m_steamID;
|
||||
CSteamID m_gsSteamID;
|
||||
uint32 m_unServerAddr;
|
||||
uint16 m_usServerPort;
|
||||
CUtlBuffer *m_pVarData;
|
||||
};
|
||||
CUtlLinkedList< StartPlayingWork_t, int > m_llStartPlaying;
|
||||
int m_nStartPlayingJobCount;
|
||||
|
||||
// URL to use for our app's CDN'd images
|
||||
mutable CUtlString m_sCDNURL;
|
||||
};
|
||||
|
||||
extern CGCBase *GGCBase();
|
||||
|
||||
EResult YieldingSendWebAPIRequest( CSteamAPIRequest &request, KeyValues *pKVResponse, CUtlString &errMsg, bool b200MeansSuccess );
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // GCBASE_H
|
||||
193
public/gcsdk/gcclient.h
Normal file
193
public/gcsdk/gcclient.h
Normal file
@@ -0,0 +1,193 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Holds the CGCClient class
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCCLIENT_H
|
||||
#define GCCLIENT_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "steam/steam_api.h"
|
||||
#include "jobmgr.h"
|
||||
#include "sharedobject.h"
|
||||
|
||||
class ISteamGameCoordinator;
|
||||
struct GCMessageAvailable_t;
|
||||
class CTestEvent;
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Interface for communicating with the GC
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCClient
|
||||
{
|
||||
public:
|
||||
CGCClient( bool bGameserver = false );
|
||||
virtual ~CGCClient( );
|
||||
|
||||
/// Call once at program startup
|
||||
bool BInit( uint32 unVersion, ISteamClient *pSteamClient, HSteamUser hSteamUser, HSteamPipe hSteamPipe );
|
||||
|
||||
/// Cleanup
|
||||
void Uninit( );
|
||||
|
||||
/// Call this to perform periodic service
|
||||
bool BMainLoop( uint64 ulLimitMicroseconds, uint64 ulFrameTimeMicroseconds = 0 );
|
||||
|
||||
/// Set current session need state value that is sent in the HELLO message to
|
||||
/// determine login priority. At this generic level we don't know what the
|
||||
/// game-specific client states mean, and which states imply a need for a
|
||||
/// GC session, so you need to tell us that, too. This decides whether we are
|
||||
/// aggressive at sending HELLO messages to try to establish the connection
|
||||
/// or not.
|
||||
void SetSessionNeed( uint32 nSessionNeed, bool bWantSession );
|
||||
|
||||
/// Launcher value. Sent in the HELLO message
|
||||
void SetLauncherType( uint32 nLauncherType ) { m_nLauncherType = nLauncherType; }
|
||||
|
||||
/// Steam datagram port, for servers. Sent in the HELLO message
|
||||
void SetServerSteamdatagramPort( uint16 usPort ) { m_usSteamdatagramPort = usPort; }
|
||||
|
||||
CJobMgr &GetJobMgr() { return m_JobMgr; }
|
||||
|
||||
/// Send a message to the GC.
|
||||
bool BSendMessage( uint32 unMsgType, const uint8 *pubData, uint32 cubData );
|
||||
bool BSendMessage( const CGCMsgBase& msg );
|
||||
bool BSendMessage( const CProtoBufMsgBase& msg );
|
||||
|
||||
/// Locate a given shared object from the cache
|
||||
CSharedObject *FindSharedObject( SOID_t ID, const CSharedObject & soIndex );
|
||||
|
||||
/// Find a shared object cache for the specified user. Optionally, the cache will be
|
||||
/// created if it doesn't not currently exist.
|
||||
CGCClientSharedObjectCache *FindSOCache( SOID_t ID, bool bCreateIfMissing = true );
|
||||
|
||||
/// Find a set of shared object caches for a specific type of SOID
|
||||
/// returns true if any were found
|
||||
typedef CUtlVectorFixedGrowable< CGCClientSharedObjectCache *, 1 > ClientSOCacheVec_t;
|
||||
bool FindSOCacheByType( uint32 type, ClientSOCacheVec_t &cacheList );
|
||||
|
||||
/// Adds a listener to the shared object cache for the specified Steam ID.
|
||||
///
|
||||
/// @see CGCClientSharedObjectCache::AddListener
|
||||
bool AddSOCacheListener( ISharedObjectListener *pListener );
|
||||
|
||||
/// Removes a listener for the shared object cache for the specified Steam ID.
|
||||
/// Returns true if we were listening and were successfully removed, false
|
||||
/// otherwise
|
||||
///
|
||||
/// @see CGCClientSharedObjectCache::RemoveListener
|
||||
bool RemoveSOCacheListener( ISharedObjectListener *pListener );
|
||||
|
||||
void DispatchSOCreated( SOID_t owner, const CSharedObject *pObject, ESOCacheEvent eEvent );
|
||||
void DispatchSOUpdated( SOID_t owner, const CSharedObject *pObject, ESOCacheEvent eEvent );
|
||||
void DispatchSODestroyed( SOID_t owner, const CSharedObject *pObject, ESOCacheEvent eEvent );
|
||||
void DispatchSOCacheSubscribed( SOID_t owner, ESOCacheEvent eEvent );
|
||||
void DispatchSOCacheUnsubscribed( SOID_t owner, ESOCacheEvent eEvent );
|
||||
|
||||
typedef CUtlVector< ISharedObjectListener * > SharedObjectListensersVec_t;
|
||||
const SharedObjectListensersVec_t & GetListeners() const { return m_vecListeners; }
|
||||
|
||||
void OnGCMessageAvailable( GCMessageAvailable_t *pCallback );
|
||||
ISteamGameCoordinator *GetSteamGameCoordinator() { return m_pSteamGameCoordinator; }
|
||||
|
||||
virtual void Test_AddEvent( CTestEvent *pEvent ) {}
|
||||
virtual void Test_CacheSubscribed( SOID_t ID ) {}
|
||||
|
||||
void NotifySOCacheUnsubscribed( SOID_t ID );
|
||||
void NotifyResubscribedUpToDate( SOID_t ID );
|
||||
|
||||
/// Send a HELLO message to the GC now.
|
||||
void SendHello();
|
||||
|
||||
// Called when we receive a welcome message, to sync up our SO caches with the
|
||||
// what the GC is telling us we have.
|
||||
void ProcessSOCacheSubscribedMsg( const CMsgSOCacheSubscribed &msg );
|
||||
|
||||
/// Simulate inability to connect to DOTA's GC.
|
||||
/// (But allow us to connect to Steam.)
|
||||
bool GetSimulateGCConnectionFailure() const { return m_bSimulateGCConnectionFailure; }
|
||||
void SetSimulateGCConnectionFailure( bool bForcedFailure );
|
||||
|
||||
/// Called when any messages times out. When this happens, it's usually
|
||||
/// safe to assume that the connection has been interrupted, and we should
|
||||
/// renegotiate.
|
||||
void MessageReplyTimedOut( uint32 nExpectedMsg, uint nTimeoutSecs );
|
||||
|
||||
//
|
||||
// Logon queue stats.
|
||||
//
|
||||
// These return negative if quantities are not known.
|
||||
// All of these numbers can only be valid if we're in the
|
||||
// GCConnectionStatus_NO_SESSION_IN_LOGON_QUEUE state. However,
|
||||
// just because we're in that state does NOT mean that they will be
|
||||
// available!
|
||||
int GetLogonQueuePosition() const { return m_nLogonQueuePosition; }
|
||||
int GetLogonQueueSize() const { return m_nLogonQueueSize; }
|
||||
int GetLogonQueueEstimatedSecondsRemaining() const;
|
||||
int GetLogonQueueApproxWaitSeconds() const;
|
||||
|
||||
protected:
|
||||
|
||||
ISteamUser *m_pSteamUser;
|
||||
ISteamGameServer *m_pSteamGameserver;
|
||||
ISteamGameCoordinator *m_pSteamGameCoordinator;
|
||||
CUtlMemory<uint8> m_memMsg;
|
||||
|
||||
// local job handling
|
||||
CJobMgr m_JobMgr;
|
||||
|
||||
// Shared object caches
|
||||
typedef CUtlMap<SOID_t, CGCClientSharedObjectCache *> MapSOCache_t;
|
||||
MapSOCache_t m_mapSOCache;
|
||||
|
||||
SharedObjectListensersVec_t m_vecListeners;
|
||||
|
||||
uint64 m_timeLastSendHello;
|
||||
uint64 m_timeReceivedConnectionStatus;
|
||||
uint64 m_timeLoggedOn;
|
||||
uint32 m_unVersion;
|
||||
const bool m_bGameserver;
|
||||
bool m_bSimulateGCConnectionFailure;
|
||||
uint32 m_nSessionNeed;
|
||||
uint32 m_nLastSessionNeed; // last session need state sent / received from the GC
|
||||
bool m_bWantSession;
|
||||
uint32 m_nLauncherType;
|
||||
uint16 m_usSteamdatagramPort;
|
||||
|
||||
int m_nLogonQueuePosition;
|
||||
int m_nLogonQueueSize;
|
||||
uint64 m_timeLogonQueueApproxTimeEnteredQueue;
|
||||
uint64 m_timeLogonQueueEstimatedTimeExitQueue;
|
||||
|
||||
void ClearLogonQueueStats();
|
||||
|
||||
void UpdateLogonState();
|
||||
void ThinkConnection();
|
||||
void DispatchPacket( IMsgNetPacket *pMsgNetPacket );
|
||||
|
||||
// Steam callback for getting notified about messages available. Not part of the class
|
||||
// in Steam builds because we use the TestClientManager instead of steam_api.dll in Steam
|
||||
#ifndef STEAM
|
||||
CCallback< CGCClient, GCMessageAvailable_t, false > m_callbackGCMessageAvailable;
|
||||
STEAM_CALLBACK( CGCClient, OnSteamServersDisconnected, SteamServersDisconnected_t, m_CallbackSteamServersDisconnected );
|
||||
STEAM_CALLBACK( CGCClient, OnSteamServerConnectFailure, SteamServerConnectFailure_t, m_CallbackSteamServerConnectFailure );
|
||||
STEAM_CALLBACK( CGCClient, OnSteamServersConnected, SteamServersConnected_t, m_CallbackSteamServersConnected );
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
//utility to make defining client jobs more straight forward
|
||||
#define GC_REG_CLIENT_JOB( JobClass, Msg ) \
|
||||
GC_REG_JOB( GCSDK::CGCClient, JobClass, #JobClass, Msg )
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCCLIENT_H
|
||||
186
public/gcsdk/gcclient_sharedobjectcache.h
Normal file
186
public/gcsdk/gcclient_sharedobjectcache.h
Normal file
@@ -0,0 +1,186 @@
|
||||
//====== 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
|
||||
{
|
||||
|
||||
class CGCClient;
|
||||
|
||||
/// 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( SOID_t owner, const CSharedObject *pObject, 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( SOID_t owner, const CSharedObject *pObject, 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( SOID_t owner, 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( SOID_t owner, 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( SOID_t owner, ESOCacheEvent eEvent ) = 0;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: The part of a shared object cache that handles all objects of a
|
||||
// single type.
|
||||
//----------------------------------------------------------------------------
|
||||
class CGCClientSharedObjectTypeCache : public CSharedObjectTypeCache
|
||||
{
|
||||
public:
|
||||
CGCClientSharedObjectTypeCache( int nTypeID );
|
||||
virtual ~CGCClientSharedObjectTypeCache();
|
||||
|
||||
bool BParseCacheSubscribedMsg( const CMsgSOCacheSubscribed_SubscribedType & msg, CUtlVector<CSharedObject*> &vecCreatedObjects, CUtlVector<CSharedObject*> &vecUpdatedObjects, CUtlVector<CSharedObject*> &vecObjectsToDestroy );
|
||||
|
||||
CSharedObject *BCreateFromMsg( const void *pvData, uint32 unSize, bool *bUpdatedExisting );
|
||||
bool BDestroyFromMsg( SOID_t owner, CGCClient &client, const void *pvData, uint32 unSize );
|
||||
bool BCreateOrUpdateFromMsg( SOID_t owner, CGCClient &client, const void *pvData, uint32 unSize );
|
||||
|
||||
void RemoveAllObjects( CUtlVector<CSharedObject*> &vecObjects );
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// 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
|
||||
{
|
||||
public:
|
||||
explicit CGCClientSharedObjectCache( SOID_t ID );
|
||||
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; }
|
||||
void SetSubscribed( bool bSubscribed ) { m_bSubscribed = bSubscribed; }
|
||||
|
||||
/// Who owns this cache?
|
||||
virtual SOID_t GetOwner() const OVERRIDE { return m_IDOwner; }
|
||||
|
||||
const CGCClientSharedObjectTypeCache *FindTypeCache( int nClassID ) const { return static_cast<const CGCClientSharedObjectTypeCache *>(FindBaseTypeCache( nClassID )); }
|
||||
CGCClientSharedObjectTypeCache *FindTypeCache( int nClassID ) { return static_cast<CGCClientSharedObjectTypeCache *>(FindBaseTypeCache( nClassID )); }
|
||||
CGCClientSharedObjectTypeCache *CreateTypeCache( int nClassID ) { return static_cast<CGCClientSharedObjectTypeCache *>(CreateBaseTypeCache( nClassID )); }
|
||||
|
||||
bool BParseCacheSubscribedMsg( CGCClient &owner, const CMsgSOCacheSubscribed & msg );
|
||||
void BuildCacheSubscribedMsg( CMsgSOCacheSubscribed &msg ) const;
|
||||
|
||||
bool BCreateFromMsg( CGCClient &owner, int nTypeID, const void *pvData, uint32 unSize );
|
||||
bool BDestroyFromMsg( CGCClient &owner, int nTypeID, const void *pvData, uint32 unSize );
|
||||
bool BUpdateFromMsg( CGCClient &owner, int nTypeID, const void *pvData, uint32 unSize );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
void NotifyCreated( ISharedObjectListener &context );
|
||||
|
||||
private:
|
||||
virtual CSharedObjectTypeCache *AllocateTypeCache( int nClassID ) const OVERRIDE { return new CGCClientSharedObjectTypeCache( nClassID ); }
|
||||
CGCClientSharedObjectTypeCache *GetTypeCacheByIndex( int nIndex ) { return (CGCClientSharedObjectTypeCache *)CSharedObjectCache::GetTypeCacheByIndex( nIndex ); }
|
||||
|
||||
SOID_t m_IDOwner;
|
||||
bool m_bInitialized;
|
||||
bool m_bSubscribed;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
|
||||
#endif //GCCLIENT_SHAREDOBJECTCACHE_H
|
||||
144
public/gcsdk/gcclientjob.h
Normal file
144
public/gcsdk/gcclientjob.h
Normal file
@@ -0,0 +1,144 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCCLIENTJOB_H
|
||||
#define GCCLIENTJOB_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
class CGCClient;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: handles a network message job from the client
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCClientJob : public CJob
|
||||
{
|
||||
public:
|
||||
CGCClientJob( CGCClient *pGCClient ) : CJob( pGCClient->GetJobMgr() ), m_pGCClient( pGCClient ), m_cHeartbeatsBeforeTimeout( k_cJobHeartbeatsBeforeTimeoutDefault ) {}
|
||||
|
||||
// all GCClient jobs must implement one of these
|
||||
virtual bool BYieldingRunGCJob( IMsgNetPacket *pNetPacket ) { return false; }
|
||||
virtual bool BYieldingRunGCJob() { return false; }
|
||||
|
||||
protected:
|
||||
CGCClient *m_pGCClient;
|
||||
|
||||
bool BYldSendMessageAndGetReply( CGCMsgBase &msgOut, uint nTimeoutSec, CGCMsgBase *pMsgIn, MsgType_t eMsg )
|
||||
{
|
||||
IMsgNetPacket *pNetPacket = NULL;
|
||||
|
||||
if ( !BYldSendMessageAndGetReply( msgOut, nTimeoutSec, &pNetPacket ) )
|
||||
return false;
|
||||
|
||||
pMsgIn->SetPacket( pNetPacket );
|
||||
|
||||
if ( pMsgIn->Hdr().m_eMsg != eMsg )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BYldSendMessageAndGetReply( CGCMsgBase &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket )
|
||||
{
|
||||
msgOut.ExpectingReply( GetJobID() );
|
||||
|
||||
if ( !m_pGCClient->BSendMessage( msgOut ) )
|
||||
return false;
|
||||
|
||||
SetJobTimeout( nTimeoutSec );
|
||||
return BYieldingWaitForMsg( ppNetPacket );
|
||||
}
|
||||
|
||||
enum BYldSendMessageAndGetReply_t
|
||||
{
|
||||
BYLDREPLY_SUCCESS,
|
||||
BYLDREPLY_SEND_FAILED,
|
||||
BYLDREPLY_TIMEOUT,
|
||||
BYLDREPLY_MSG_TYPE_MISMATCH,
|
||||
};
|
||||
BYldSendMessageAndGetReply_t BYldSendMessageAndGetReplyEx( CProtoBufMsgBase &msgOut, uint nTimeoutSec, CProtoBufMsgBase *pMsgIn, MsgType_t eMsg )
|
||||
{
|
||||
IMsgNetPacket *pNetPacket = NULL;
|
||||
|
||||
msgOut.ExpectingReply( GetJobID() );
|
||||
|
||||
if ( !m_pGCClient->BSendMessage( msgOut ) )
|
||||
return BYLDREPLY_SEND_FAILED;
|
||||
|
||||
SetJobTimeout( nTimeoutSec );
|
||||
if( !BYieldingWaitForMsg( &pNetPacket ) )
|
||||
return BYLDREPLY_TIMEOUT;
|
||||
|
||||
if( !pMsgIn->InitFromPacket( pNetPacket ) )
|
||||
return BYLDREPLY_MSG_TYPE_MISMATCH;
|
||||
|
||||
if ( pMsgIn->GetEMsg() != eMsg )
|
||||
return BYLDREPLY_MSG_TYPE_MISMATCH;
|
||||
|
||||
return BYLDREPLY_SUCCESS;
|
||||
}
|
||||
|
||||
bool BYldSendMessageAndGetReply( CProtoBufMsgBase &msgOut, uint nTimeoutSec, CProtoBufMsgBase *pMsgIn, MsgType_t eMsg )
|
||||
{
|
||||
BYldSendMessageAndGetReply_t result = BYldSendMessageAndGetReplyEx( msgOut, nTimeoutSec, pMsgIn, eMsg );
|
||||
if ( result == BYLDREPLY_SUCCESS )
|
||||
return true;
|
||||
|
||||
// Notify the client if the reply times out.
|
||||
if ( result == BYLDREPLY_TIMEOUT )
|
||||
m_pGCClient->MessageReplyTimedOut( eMsg, nTimeoutSec );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BYldSendMessageAndGetReply( CProtoBufMsgBase &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket )
|
||||
{
|
||||
msgOut.ExpectingReply( GetJobID() );
|
||||
|
||||
if ( !m_pGCClient->BSendMessage( msgOut ) )
|
||||
return false;
|
||||
|
||||
SetJobTimeout( nTimeoutSec );
|
||||
return BYieldingWaitForMsg( ppNetPacket );
|
||||
}
|
||||
|
||||
virtual uint32 CHeartbeatsBeforeTimeout() { return m_cHeartbeatsBeforeTimeout; }
|
||||
void SetJobTimeout( uint nTimeoutSec ) { m_cHeartbeatsBeforeTimeout = 1 + ((nTimeoutSec * k_nMillion) / k_cMicroSecJobHeartbeat); }
|
||||
|
||||
private:
|
||||
virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket )
|
||||
{
|
||||
// Protection against a NULL GCClient. Yields so the job is not deleted instantly
|
||||
if ( !m_pGCClient )
|
||||
{
|
||||
BYieldingWaitOneFrame();
|
||||
return false;
|
||||
}
|
||||
|
||||
return BYieldingRunGCJob( pNetPacket );
|
||||
}
|
||||
|
||||
virtual bool BYieldingRunJob( void *pvStartParam )
|
||||
{
|
||||
// Protection against a NULL GCClient. Yields so the job is not deleted instantly
|
||||
if ( !m_pGCClient )
|
||||
{
|
||||
BYieldingWaitOneFrame();
|
||||
return false;
|
||||
}
|
||||
|
||||
return BYieldingRunGCJob();
|
||||
}
|
||||
|
||||
uint32 m_cHeartbeatsBeforeTimeout;
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCCLIENTJOB_H
|
||||
70
public/gcsdk/gcclientsdk.h
Normal file
70
public/gcsdk/gcclientsdk.h
Normal file
@@ -0,0 +1,70 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: includes all the headers required for the cliend side of the GC
|
||||
// SDK GC SDK. Include this in your stdafx.h
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCCLIENTSDK_H
|
||||
#define GCCLIENTSDK_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "steam/steamtypes.h"
|
||||
|
||||
#include "tier0/dbg.h"
|
||||
#include "tier0/vprof.h"
|
||||
#include "tier0/fasttimer.h"
|
||||
#include "tier0/t0constants.h"
|
||||
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier1/utllinkedlist.h"
|
||||
#include "tier1/utlpriorityqueue.h"
|
||||
#include "tier1/utlstring.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
#include "tier1/mempool.h"
|
||||
#include "tier1/tsmempool.h"
|
||||
#include "tier1/tsmultimempool.h"
|
||||
#include "tier1/checksum_crc.h"
|
||||
#include "tier1/fmtstr.h"
|
||||
|
||||
#include "vstdlib/coroutine.h"
|
||||
|
||||
// public stuff
|
||||
#include "steam/steamclientpublic.h"
|
||||
#include "steam/isteamclient.h"
|
||||
#include "steam/isteamgamecoordinator.h"
|
||||
#include "steam/steam_api.h"
|
||||
|
||||
// stuff to include early because it is widely depended on
|
||||
#include "netpacket.h"
|
||||
#include "gcmsg.h"
|
||||
#include "msgprotobuf.h"
|
||||
#include "gcconstants.h"
|
||||
#include "refcount.h"
|
||||
|
||||
#include "jobtime.h"
|
||||
#include "messagelist.h"
|
||||
#include "gclogger.h"
|
||||
#include "job.h"
|
||||
#include "jobmgr.h"
|
||||
#include "netpacketpool.h"
|
||||
#include "soid.h"
|
||||
#include "sharedobject.h"
|
||||
#include "protobufsharedobject.h"
|
||||
#include "sharedobjectcache.h"
|
||||
#include "gcclient_sharedobjectcache.h"
|
||||
#include "gcclient.h"
|
||||
#include "gcclientjob.h"
|
||||
|
||||
// this is a hack to make sure that the funny Valve override of
|
||||
// offsetof survives the STL includes that happened because of the
|
||||
// protobuf includes above.
|
||||
#ifdef LINUX
|
||||
#undef offsetof
|
||||
#define offsetof(s,m) (size_t)&(((s *)0)->m)
|
||||
#endif
|
||||
|
||||
#endif // GCCLIENTSDK_H
|
||||
176
public/gcsdk/gcconstants.h
Normal file
176
public/gcsdk/gcconstants.h
Normal file
@@ -0,0 +1,176 @@
|
||||
//========= Copyright ©, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: declares a variety of constants
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCCONSTANTS_H
|
||||
#define GCCONSTANTS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "steam/steamtypes.h"
|
||||
#include "tier0/t0constants.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Timing constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// How long each frame should last
|
||||
const int k_cMicroSecPerShellFrame = 50 * k_nThousand;
|
||||
|
||||
// How many frames per second should we have
|
||||
const uint32 k_cFramesPerSec = k_nMillion / k_cMicroSecPerShellFrame;
|
||||
|
||||
// Task granularity -- the longest time in microseconds you should spend without yielding
|
||||
const int k_cMicroSecTaskGranularity = 5 * k_nThousand;
|
||||
|
||||
// If a frame runs longer than it's supposed to (which they always will by at least a little),
|
||||
// we subtract the overage from the next frame. This is the maximum amount to correct each frame.
|
||||
const int k_cMicroSecMaxFrameCorrection = 25 * k_nThousand;
|
||||
|
||||
// if server time is too far behind, we will start skipping frames to re-synchronize it with real time
|
||||
const int k_cMicroSecBehindToStartFrameSkipping = (k_nMillion * 2);
|
||||
|
||||
// Default Max time to allow a job to be blocked on I/O
|
||||
const int k_cMicroSecJobPausedTimeout = 20 * k_nMillion;
|
||||
|
||||
// How much time a job should run before heartbeating
|
||||
const int k_cMicroSecJobHeartbeat = k_cMicroSecJobPausedTimeout / 4;
|
||||
|
||||
// Default Max number of job heartbeat intervals to allow a job to be blocked on I/O
|
||||
const int k_cJobHeartbeatsBeforeTimeoutDefault = k_cMicroSecJobPausedTimeout / k_cMicroSecJobHeartbeat;
|
||||
|
||||
// Number of seconds to take to cycle through all active sessions
|
||||
const int k_nUserSessionRunInterval = 5 * k_nMillion;
|
||||
const int k_nGSSessionRunInterval = 5 * k_nMillion;
|
||||
const int k_nAccountDetailsRunInterval = 30 * k_nMillion;
|
||||
const int k_nLocksRunInterval = 120 * k_nMillion;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Spew / EmitEvent constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define SPEW_ALWAYS 1
|
||||
#define SPEW_NEVER 5
|
||||
|
||||
#define LOG_ALWAYS 1
|
||||
#define LOG_NEVER 5
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// string constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const char k_rgchUnknown[] = "Unknown";
|
||||
|
||||
#ifdef GC
|
||||
//-----------------------------------------------------------------------------
|
||||
// SQL constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const int k_cSQLObjectNameMax = 128; // max length of a SQL identifier (column name, index name, table name ... )
|
||||
const int k_cchSQLStatementTextMax = 8192; // nominal max length of a SQL statement
|
||||
const uint32 k_cubRecordMax = 4 * k_nMillion; // Max size of a single record
|
||||
const uint32 k_cubVarFieldMax = 1 * k_nMillion; // Max size of a variable-length field
|
||||
const int k_cMaxCol = 50;
|
||||
const uint32 k_cMaxBindParams = 500; // the largest number of bind parameters allowed in a single query
|
||||
|
||||
// iTable constants
|
||||
const int k_iTableNil = -1; // No table at all
|
||||
const int k_iFieldNil = -1; // No field at all
|
||||
|
||||
|
||||
enum EForeignKeyAction
|
||||
{
|
||||
k_EForeignKeyActionNoAction = 0,
|
||||
k_EForeignKeyActionCascade = 1,
|
||||
k_EForeignKeyActionSetNULL = 2,
|
||||
};
|
||||
const char *PchNameFromEForeignKeyAction( EForeignKeyAction eForeignKeyAction );
|
||||
EForeignKeyAction EForeignKeyActionFromName( const char *pchName );
|
||||
|
||||
const char *PchNameFromEGCSQLType( EGCSQLType eForeignKeyAction );
|
||||
|
||||
#ifndef SQLRETURN
|
||||
typedef short SQLRETURN;
|
||||
#endif // SQLRETURN
|
||||
#endif // GC
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// WebAPI constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const uint32 k_cubWebAPIKey = 16; // size of the web api key
|
||||
const uint32 k_cchWebAPIKeyStringMax = (k_cubWebAPIKey * 2) + 1; // size of string representing web API key (hex encoded so twice the size + NUL)
|
||||
typedef unsigned char WebAPIKey_t[ k_cubWebAPIKey ];
|
||||
|
||||
// used to revoke WebAPI keys. Stored in the database... do not reorder.
|
||||
enum EWebAPIKeyStatus
|
||||
{
|
||||
k_EWebAPIKeyValid = 0,
|
||||
k_EWebAPIKeyRevoked = 1,
|
||||
|
||||
k_EWebAPIKeyInvalid = 255,
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Game Server constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const uint32 k_cubGameServerToken = 16; // size of the game server identity token key
|
||||
const uint32 k_cchGameServerTokenStringMax = (k_cubGameServerToken * 2) + 1; // size of string representing game server identity token (hex encoded so twice the size + NUL)
|
||||
typedef unsigned char GameServerIdentityToken_t[ k_cubGameServerToken ];
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Account linkage (constants from Steam)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const uint32 k_unInvalidPerfectWorldID = 0;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Other constants
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const int k_cSmallBuff = 255; // smallish buffer
|
||||
const int k_cMedBuff = 1024; // medium buffer
|
||||
|
||||
const int k_cubMsgSizeSmall = 512; // small sized packet
|
||||
|
||||
const int k_cInitialNetworkBuffers = 10; // # of network buffers to see the system with
|
||||
|
||||
const int k_cubMaxExpectedMsgDataSize = 5 * k_nMegabyte;// the maximum application data that we EXPECT to be sent in a single message
|
||||
|
||||
#ifndef STRINGIFY
|
||||
#define STRINGIFY(x) #x
|
||||
#endif // CS:GO already defines STRINGIFY appropriately, no reason to redefine here
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
#define FILE_AND_LINE __FILE__ ":" TOSTRING(__LINE__)
|
||||
|
||||
// Default capacity for session hash tables
|
||||
const int k_cGCUserSessionInit = 10 * k_nThousand; // Can grow indefinitely by this increment
|
||||
const int k_cBucketGCUserSession = 100 * k_nThousand; // Fixed size
|
||||
const int k_cGCGSSessionInit = 5 * k_nThousand; // Can grow indefinitely by this increment
|
||||
const int k_cBucketGCGSSession = 50 * k_nThousand; // Fixed size
|
||||
const int k_cAccountDetailsInit = 10 * k_nThousand; // Can grow indefinitely by this increment
|
||||
const int k_cBucketAccountDetails = 100 * k_nThousand; // Fixed size
|
||||
const int k_cPersonaNamesInit = 50 * k_nThousand; // Can grow indefinitely by this increment
|
||||
const int k_cBucketPersonaNames = 250 * k_nThousand; // Fixed size
|
||||
const int k_cGCLocksInit = 50 * k_nThousand; // Can grow indefinitely by this increment
|
||||
const int k_cBucketGCLocks = 500 * k_nThousand; // Fixed size
|
||||
|
||||
const char *PchNameFromEUniverse( EUniverse eUniverse );
|
||||
EUniverse EUniverseFromName( const char *pchName );
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCCONSTANTS_H
|
||||
81
public/gcsdk/gcdirtyfield.h
Normal file
81
public/gcsdk/gcdirtyfield.h
Normal file
@@ -0,0 +1,81 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Network dirty field marker for shared objects
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GC_DIRTYFIELD_H
|
||||
#define GC_DIRTYFIELD_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
//#include "sharedobject.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CSharedObject;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Holds the set of dirty fields for this object
|
||||
//----------------------------------------------------------------------------
|
||||
class CSharedObjectDirtyFieldList
|
||||
{
|
||||
public:
|
||||
CSharedObjectDirtyFieldList( CSharedObject *obj );
|
||||
~CSharedObjectDirtyFieldList();
|
||||
|
||||
CSharedObject *Obj() const;
|
||||
void DirtyField( int index );
|
||||
void GetDirtyFieldSet( CUtlVector<int> &fieldSet ) const;
|
||||
|
||||
private:
|
||||
CSharedObject *m_obj;
|
||||
uint32 m_firstFieldBits;
|
||||
CUtlVector<int> *m_pExtendedFields;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Holds a list of dirty fields on objects
|
||||
//----------------------------------------------------------------------------
|
||||
class CSharedObjectDirtyList
|
||||
{
|
||||
public:
|
||||
CSharedObjectDirtyList();
|
||||
~CSharedObjectDirtyList();
|
||||
|
||||
void DirtyObjectField( CSharedObject *obj, int field );
|
||||
|
||||
int InvalidIndex() const;
|
||||
int NumDirtyObjects() const;
|
||||
int FindIndexByObj( CSharedObject *pObj );
|
||||
bool GetDirtyFieldSetByIndex( int index, CSharedObject **ppObj, CUtlVector<int> &fieldSet ) const;
|
||||
bool GetDirtyFieldSetByObj( CSharedObject *pObj, CUtlVector<int> &fieldSet );
|
||||
bool FindAndRemove( CSharedObject *pObj );
|
||||
void RemoveAll();
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
CUtlVector< CSharedObjectDirtyFieldList > m_sharedObjectDirtyFieldList;
|
||||
};
|
||||
|
||||
inline int CSharedObjectDirtyList::InvalidIndex() const
|
||||
{
|
||||
return m_sharedObjectDirtyFieldList.InvalidIndex();
|
||||
}
|
||||
|
||||
inline int CSharedObjectDirtyList::NumDirtyObjects() const
|
||||
{
|
||||
return m_sharedObjectDirtyFieldList.Count();
|
||||
}
|
||||
|
||||
} // GCSDK
|
||||
|
||||
|
||||
#endif //GC_DIRTYFIELD_H
|
||||
24
public/gcsdk/gchost.h
Normal file
24
public/gcsdk/gchost.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Holds a pointer to the GC's host's interface
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCHOST_H
|
||||
#define GCHOST_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gamecoordinator/igamecoordinatorhost.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
|
||||
extern IGameCoordinatorHost *GGCHost();
|
||||
extern void SetGCHost( IGameCoordinatorHost *pHost );
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCHOST_H
|
||||
180
public/gcsdk/gcjob.h
Normal file
180
public/gcsdk/gcjob.h
Normal file
@@ -0,0 +1,180 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCJOB_H
|
||||
#define GCJOB_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gcwebapikey.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: handles a network message job from the client
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCJob : public CJob
|
||||
{
|
||||
public:
|
||||
CGCJob( CGCBase *pGC ) : CJob( pGC->GetJobMgr() ), m_pGC( pGC ), m_cHeartbeatsBeforeTimeout( k_cJobHeartbeatsBeforeTimeoutDefault ) {}
|
||||
|
||||
// all GC jobs must implement one of these
|
||||
virtual bool BYieldingRunGCJob( IMsgNetPacket *pNetPacket ) { return false; }
|
||||
virtual bool BYieldingRunGCJob() { return false; }
|
||||
|
||||
virtual EServerType GetServerType() { return k_EServerTypeGC; }
|
||||
|
||||
bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, CGCMsgBase &msgOut, uint nTimeoutSec, CGCMsgBase *pMsgIn, MsgType_t eMsg );
|
||||
bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, CGCMsgBase &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket );
|
||||
bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, IProtoBufMsg &msgOut, uint nTimeoutSec, IProtoBufMsg *pMsgIn, MsgType_t eMsg );
|
||||
bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, IProtoBufMsg &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket );
|
||||
|
||||
virtual uint32 CHeartbeatsBeforeTimeout() { return m_cHeartbeatsBeforeTimeout; }
|
||||
void SetJobTimeout( uint nTimeoutSec ) { m_cHeartbeatsBeforeTimeout = 1 + ((nTimeoutSec * k_nMillion) / k_cMicroSecJobHeartbeat); }
|
||||
|
||||
protected:
|
||||
CGCBase *m_pGC;
|
||||
|
||||
private:
|
||||
virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket )
|
||||
{
|
||||
return BYieldingRunGCJob( pNetPacket );
|
||||
}
|
||||
|
||||
virtual bool BYieldingRunJob( void *pvStartParam )
|
||||
{
|
||||
return BYieldingRunGCJob();
|
||||
}
|
||||
|
||||
uint32 m_cHeartbeatsBeforeTimeout;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CGCWGJob - A job invoked from forwarded WG messages
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCWGJob : public CGCJob
|
||||
{
|
||||
public:
|
||||
CGCWGJob( CGCBase *pGCBase );
|
||||
~CGCWGJob();
|
||||
bool BYieldingRunGCJob( IMsgNetPacket * pNetPacket ); // invokes BYieldingRunJobFromRequest
|
||||
virtual bool BYieldingRunJobFromRequest( KeyValues *pkvRequest, KeyValues *pkvResponse ) = 0;
|
||||
virtual bool BVerifyParams( const CGCMsg<MsgGCWGRequest_t> & msg, KeyValues *pkvRequest, const WebApiFunc_t * pWebApiFunc );
|
||||
void SetWebApiFunc( const WebApiFunc_t * pWebApiFunc ) { m_pWebApiFunc = pWebApiFunc; }
|
||||
|
||||
void SetErrorMessage( KeyValues *pkvErr, const char *pchErrorMsg, int32 nResult );
|
||||
|
||||
protected:
|
||||
KeyValues *m_pkvResponse;
|
||||
CUtlBuffer m_bufRequest; // packed binary form of the request
|
||||
const WebApiFunc_t * m_pWebApiFunc;
|
||||
|
||||
// requester auth info, like WGRequestContext
|
||||
CSteamID m_steamID;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CGCJobVerifySession - A job that asks steam if a given user is still connected
|
||||
// and cleans up the session if the user is gone
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCJobVerifySession : public CGCJob
|
||||
{
|
||||
public:
|
||||
CGCJobVerifySession( CGCBase *pGC, const CSteamID &steamID ) : CGCJob( pGC ), m_steamID( steamID ) { }
|
||||
virtual bool BYieldingRunGCJob();
|
||||
|
||||
private:
|
||||
CSteamID m_steamID;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CWebAPIJob - A job invoked from a forwarded WebAPI request
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWebAPIJob : public CGCJob
|
||||
{
|
||||
public:
|
||||
CWebAPIJob( CGCBase *pGC, EWebAPIOutputFormat eDefaultOutputFormat = k_EWebAPIOutputFormat_JSON );
|
||||
~CWebAPIJob();
|
||||
|
||||
// Called by jobmgr, and then invokes appropriate run function for specific API version requested
|
||||
bool BYieldingRunJobFromMsg( IMsgNetPacket * pNetPacket );
|
||||
|
||||
// Implemented by each individual WebAPI job, should be declared using BEGIN_WEBAPI_JOB_VERSION_ADAPTERS and related macros.
|
||||
virtual bool BYieldingRunJobFromAPIRequest( const char *pchInterface, const char *pchMethod, uint32 unVersion, CHTTPRequest *pRequest, CHTTPResponse *pResponse, CWebAPIResponse *pWebAPIResponse ) = 0;
|
||||
|
||||
protected:
|
||||
static void AddLocalizedString( CWebAPIValues *pOutDefn, const char *pchFieldName, const char *pchKeyName, ELanguage eLang, bool bReturnTokenIfNotFound = true );
|
||||
|
||||
CHTTPRequest *m_pRequest;
|
||||
CHTTPResponse *m_pResponse;
|
||||
CWebAPIKey m_webAPIKey;
|
||||
EWebAPIOutputFormat m_eDefaultOutputFormat;
|
||||
};
|
||||
|
||||
|
||||
#define BEGIN_WEBAPI_JOB_VERSION_ADAPTERS( interface, method ) \
|
||||
bool BYieldingRunJobFromAPIRequest( const char *pchInterface, const char *pchMethod, uint32 unVersion, CHTTPRequest *pRequest, CHTTPResponse *pResponse, CWebAPIResponse *pWebAPIResponse ) \
|
||||
{ \
|
||||
if ( Q_strnicmp( pchInterface, interface, Q_strlen( interface ) ) != 0 ) \
|
||||
{ \
|
||||
AssertMsg2( false, "WebAPIJob recieved request for unexpected interface (got %s, expected %s)!", pchInterface, interface ); \
|
||||
pResponse->SetStatusCode( k_EHTTPStatusCode500InternalServerError ); \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
if ( Q_stricmp( pchMethod, method ) != 0 ) \
|
||||
{ \
|
||||
AssertMsg2( false, "WebAPIJob received request fo unexpected method (got %s, expected %s)!", pchMethod, method ); \
|
||||
pResponse->SetStatusCode( k_EHTTPStatusCode500InternalServerError ); \
|
||||
return false; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
bool bFoundVersion = false; \
|
||||
bool bResult = false; \
|
||||
switch( unVersion ) \
|
||||
{
|
||||
|
||||
#define WEBAPI_JOB_VERSION_ADAPTER( version, funcname ) \
|
||||
case version: \
|
||||
bFoundVersion = true; \
|
||||
{ \
|
||||
VPROF_BUDGET( #funcname, VPROF_BUDGETGROUP_JOBS_COROUTINES ); \
|
||||
bResult = this->##funcname( pRequest, pWebAPIResponse ); \
|
||||
} \
|
||||
break;
|
||||
|
||||
#define WEBAPI_JOB_VERSION_ADAPTER_EXTENDED_ARRAYS( version, funcname ) \
|
||||
case version: \
|
||||
bFoundVersion = true; \
|
||||
pWebAPIResponse->SetExtendedArrays( true ); \
|
||||
{ \
|
||||
VPROF_BUDGET( #funcname, VPROF_BUDGETGROUP_JOBS_COROUTINES ); \
|
||||
bResult = this->##funcname( pRequest, pWebAPIResponse ); \
|
||||
} \
|
||||
break;
|
||||
|
||||
#define END_WEBAPI_JOB_VERSION_ADAPTERS() \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
AssertMsg3( bFoundVersion, "WebAPIJob for %s/%s received unhandled version %d", pchInterface, pchMethod, unVersion ); \
|
||||
return bResult; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
|
||||
#endif // GCJOB_H
|
||||
56
public/gcsdk/gcleaderboardapi.h
Normal file
56
public/gcsdk/gcleaderboardapi.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//========= Copyright (c), Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: API to interact with Steam leaderboards on the GC.
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCLEADERBOARDAPI_H
|
||||
#define GCLEADERBOARDAPI_H
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
class CGCBase;
|
||||
|
||||
/**
|
||||
* Yielding call that attempts to find a leaderboard by name, creating one if necessary.
|
||||
* @param pName
|
||||
* @param eLeaderboardSortMethod
|
||||
* @param eLeaderboardDisplayType
|
||||
* @param bCreateIfNotFound
|
||||
* @return 0 if the leaderboard was not found, > 0 otherwise
|
||||
*/
|
||||
uint32 Leaderboard_YieldingFind( const char *pName, ELeaderboardSortMethod eLeaderboardSortMethod, ELeaderboardDisplayType eLeaderboardDisplayType, bool bCreateIfNotFound );
|
||||
|
||||
/**
|
||||
* Yielding call that attempts to set the score for the steamID in the leaderboard.
|
||||
* @param unLeaderboardID
|
||||
* @param steamID
|
||||
* @param eLeaderboardUploadScoreMethod
|
||||
* @param score
|
||||
* @param pDetails
|
||||
* @param unDetailsLength
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
bool Leaderboard_YieldingSetScore( uint32 unLeaderboardID, const CSteamID &steamID, ELeaderboardUploadScoreMethod eLeaderboardUploadScoreMethod, int score, uint8* pDetails = 0, uint32 unDetailsLength = 0 );
|
||||
|
||||
/**
|
||||
* @param pKVOutput
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
bool Leaderboard_YieldingGetLeaderboardsForGame( KeyValuesAD* pKVOutput );
|
||||
|
||||
/**
|
||||
* Yielding call that attempts to get leaderboard entries by leaderboard name
|
||||
* @param unRangeStart
|
||||
* @param unRangeEnd
|
||||
* @param pSteamID
|
||||
* @param unLeaderboardID
|
||||
* @param eDataRequestType
|
||||
* @param pKVOutput
|
||||
* @return number of entries found from specified range.
|
||||
*/
|
||||
bool Leaderbaord_YieldingGetLeaderboardEntries( int32 nRangeStart, int32 nRangeEnd, CSteamID* pSteamID, int32 unLeaderboardID, ELeaderboardDataRequest eDataRequestType, KeyValuesAD* pKVOutput );
|
||||
}; // namespace GCSDK
|
||||
|
||||
#endif // GCLEADERBOARDAPI_H
|
||||
139
public/gcsdk/gclogger.h
Normal file
139
public/gcsdk/gclogger.h
Normal file
@@ -0,0 +1,139 @@
|
||||
//========= Copyright (c), Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCLOGGER_H
|
||||
#define GCLOGGER_H
|
||||
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
//a class that defines an output group for messages that can be output to the console or to a log. This allows for individual filtering of different groups to different
|
||||
//outputs to control log spamming
|
||||
class CGCEmitGroup
|
||||
{
|
||||
public:
|
||||
|
||||
//the different severity levels for a message
|
||||
enum EMsgLevel
|
||||
{
|
||||
kMsg_Error = 1,
|
||||
kMsg_Warning = 2,
|
||||
kMsg_Msg = 3,
|
||||
kMsg_Verbose = 4
|
||||
};
|
||||
|
||||
//constructs a group given a static string for the name of the group, and the name for the console variables that control the console and log output levels. Note
|
||||
//that to help with consistency, DECLARE_GC_EMIT_GROUP should be used for declaring these objects over manually providing names for all fields
|
||||
CGCEmitGroup( const char* pszGroupName, const char* pszConsoleVar, const char* pszLogVar, const char* pszDefaultConsole, const char* pszDefaultLog )
|
||||
: m_pszGroupName( pszGroupName )
|
||||
#ifdef GC
|
||||
, m_LogLevel( pszLogVar, pszDefaultLog, 0, "The output level to log for this system. 1 means log only critical errors, 2 means log errors and warnings, 3 means log errors, warnings and messages, 4 means log everything", true, 0.0f, true, 4.0f )
|
||||
, m_ConsoleLevel( pszConsoleVar, pszDefaultConsole, 0, "The output level to log for this system. 1 means log only critical errors, 2 means log errors and warnings, 3 means log errors, warnings and messages, 4 means log everything", true, 0.0f, true, 4.0f )
|
||||
#endif
|
||||
{}
|
||||
|
||||
//get the name of the group and current filter levels
|
||||
const char* GetName() const { return m_pszGroupName; }
|
||||
#ifdef GC
|
||||
int GetConsoleLevel() const { return m_ConsoleLevel.GetInt(); }
|
||||
int GetLogLevel() const { return m_LogLevel.GetInt(); }
|
||||
#else
|
||||
int GetConsoleLevel() const { return 3; }
|
||||
int GetLogLevel() const { return 3; }
|
||||
#endif
|
||||
|
||||
//these will output text for each of the appropriate severities, from Verbose (defaulted to filtered out), to critical error. These are not intended to be called directly, as the preparation
|
||||
//of all the command line arguments can be costly, and the EG_XXXX macros should be used instead to avoid this cost.
|
||||
void Internal_AssertError( PRINTF_FORMAT_STRING const char *pchMsg, ... ) const FMTFUNCTION( 2, 3 );
|
||||
void Internal_Error( PRINTF_FORMAT_STRING const char *pchMsg, ... ) const FMTFUNCTION( 2, 3 );
|
||||
void Internal_Warning( PRINTF_FORMAT_STRING const char *pchMsg, ... ) const FMTFUNCTION( 2, 3 );
|
||||
void Internal_Msg( PRINTF_FORMAT_STRING const char *pchMsg, ... ) const FMTFUNCTION( 2, 3 );
|
||||
void Internal_Verbose( PRINTF_FORMAT_STRING const char *pchMsg, ... ) const FMTFUNCTION( 2, 3 );
|
||||
void Internal_Emit( EMsgLevel eLvl, PRINTF_FORMAT_STRING const char *pchMsg, ... ) const FMTFUNCTION( 3, 4 );
|
||||
|
||||
//same as the above, but takes a var args structure
|
||||
void AssertErrorV( PRINTF_FORMAT_STRING const char *pchMsg, va_list vaArgs ) const;
|
||||
void ErrorV( PRINTF_FORMAT_STRING const char *pchMsg, va_list vaArgs ) const;
|
||||
void WarningV( PRINTF_FORMAT_STRING const char *pchMsg, va_list vaArgs ) const;
|
||||
void MsgV( PRINTF_FORMAT_STRING const char *pchMsg, va_list vaArgs ) const;
|
||||
void VerboseV( PRINTF_FORMAT_STRING const char *pchMsg, va_list vaArgs ) const;
|
||||
void EmitV( EMsgLevel eLvl, PRINTF_FORMAT_STRING const char *pchMsg, va_list vaArgs ) const;
|
||||
|
||||
private:
|
||||
//the display name of this group, must be a static string
|
||||
const char* m_pszGroupName;
|
||||
#ifdef GC
|
||||
//the console variable used to control the log level that will be recorded to the ouput logs [0..4]
|
||||
GCConVar m_LogLevel;
|
||||
//the console variable used to control the level of output that will be displayed to the console [0..4]
|
||||
GCConVar m_ConsoleLevel;
|
||||
#endif
|
||||
};
|
||||
|
||||
//macros to emit to an emit group. These should be used so that you don't have to pay the cost of formatting parameters that won't be used if the output isn't turned on anyway.
|
||||
//Note the use of do{}while, this to prevent the if from pairing with a following else if they don't use braces around the print
|
||||
#define EG_ASSERTERROR( EmitGroup, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Error || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Error ) EmitGroup.Internal_AssertError( __VA_ARGS__ ); } while(0)
|
||||
#define EG_ERROR( EmitGroup, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Error || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Error ) EmitGroup.Internal_Error( __VA_ARGS__ ); } while(0)
|
||||
#define EG_WARNING( EmitGroup, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Warning || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Warning ) EmitGroup.Internal_Warning( __VA_ARGS__ ); } while(0)
|
||||
#define EG_MSG( EmitGroup, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Msg || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Msg ) EmitGroup.Internal_Msg( __VA_ARGS__ ); } while(0)
|
||||
#define EG_VERBOSE( EmitGroup, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Verbose || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Verbose ) EmitGroup.Internal_Verbose( __VA_ARGS__ ); } while(0)
|
||||
#define EG_EMIT( EmitGroup, eLvl, ... ) do{ if( EmitGroup.GetConsoleLevel() >= eLvl || EmitGroup.GetLogLevel() >= eLvl ) EmitGroup.Internal_Emit( eLvl, __VA_ARGS__ ); } while(0)
|
||||
|
||||
#ifdef GC
|
||||
|
||||
// Return true if we have extra logging enabled for the specified Steam ID.
|
||||
extern bool BDetailedLoggingEnabledForAnySteamID();
|
||||
extern bool BDetailedLoggingEnabledForSteamID( CSteamID steamID );
|
||||
extern bool BDetailedLoggingEnabledForAccountID( AccountID_t nAccountID );
|
||||
|
||||
// Emit info about a particular steam ID. This will emit/log if the level is enabled for
|
||||
// the specified spew group, *or* if debugging is enabled for that particular SteamID.
|
||||
#define EG_MSG_STEAMID( EmitGroup, steamID, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Msg || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Msg || BDetailedLoggingEnabledForSteamID( steamID ) ) EmitGroup.Internal_Msg( __VA_ARGS__ ); } while(0)
|
||||
#define EG_VERBOSE_STEAMID( EmitGroup, steamID, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Verbose || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Verbose || BDetailedLoggingEnabledForSteamID( steamID ) ) EmitGroup.Internal_Verbose( __VA_ARGS__ ); } while(0)
|
||||
#define EG_MSG_ACCOUNTID( EmitGroup, accountID, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Msg || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Msg || BDetailedLoggingEnabledForAccountID( accountID ) ) EmitGroup.Internal_Msg( __VA_ARGS__ ); } while(0)
|
||||
#define EG_VERBOSE_ACCOUNTID( EmitGroup, accountID, ... ) do{ if( EmitGroup.GetConsoleLevel() >= CGCEmitGroup::kMsg_Verbose || EmitGroup.GetLogLevel() >= CGCEmitGroup::kMsg_Verbose || BDetailedLoggingEnabledForAccountID( accountID ) ) EmitGroup.Internal_Verbose( __VA_ARGS__ ); } while(0)
|
||||
|
||||
#endif
|
||||
|
||||
//----------------------------------------
|
||||
//Legacy interface support - Where possible, use the CGCEmitGroup above for more robust functionality
|
||||
//----------------------------------------
|
||||
extern CGCEmitGroup SPEW_SYSTEM_MISC;
|
||||
extern CGCEmitGroup SPEW_JOB;
|
||||
extern CGCEmitGroup SPEW_CONSOLE;
|
||||
extern CGCEmitGroup SPEW_GC;
|
||||
extern CGCEmitGroup SPEW_SQL;
|
||||
extern CGCEmitGroup SPEW_NETWORK;
|
||||
extern CGCEmitGroup SPEW_SHAREDOBJ;
|
||||
extern CGCEmitGroup SPEW_MICROTXN;
|
||||
extern CGCEmitGroup SPEW_PROMO;
|
||||
extern CGCEmitGroup SPEW_PKGITEM;
|
||||
extern CGCEmitGroup SPEW_ECONOMY;
|
||||
extern CGCEmitGroup SPEW_THREADS;
|
||||
|
||||
//this one is a macro since it is called in many places and can add considerable overhead to the formatting
|
||||
void EGInternal_EmitInfo( const CGCEmitGroup& Group, int iLevel, int iLevelLog, PRINTF_FORMAT_STRING const char *pchMsg, ... ) FMTFUNCTION( 4, 5 );
|
||||
#define EmitInfo( EmitGroup, ConsoleLevel, LogLevel, ... ) do{ if( EmitGroup.GetConsoleLevel() >= ( ConsoleLevel ) || EmitGroup.GetLogLevel() >= ( LogLevel ) ) EGInternal_EmitInfo( EmitGroup, ConsoleLevel, LogLevel, __VA_ARGS__ ); } while(0)
|
||||
|
||||
void EmitInfoV( const CGCEmitGroup& Group, int iLevel, int iLevelLog, PRINTF_FORMAT_STRING const char *pchMsg, va_list vaArgs );
|
||||
|
||||
void EmitWarning( const CGCEmitGroup& Group, int iLevel, PRINTF_FORMAT_STRING const char *pchMsg, ... ) FMTFUNCTION( 3, 4 );
|
||||
void EmitError( const CGCEmitGroup& Group, PRINTF_FORMAT_STRING const char *pchMsg, ... ) FMTFUNCTION( 2, 3 );
|
||||
// Emit an assert-like error, generating a minidump
|
||||
void EmitAssertError( const CGCEmitGroup& Group, PRINTF_FORMAT_STRING const char *pchMsg, ... ) FMTFUNCTION( 2, 3 );
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
//the use of the typedef is to catch issues with people putting quotes around the group name. Note that the emit group comes first in case they precede this with 'static'
|
||||
#define DECLARE_GC_EMIT_GROUP_DEFAULTS( VarName, GroupName, ConsoleLevel, LogLevel ) \
|
||||
GCSDK::CGCEmitGroup VarName( #GroupName, "console_level_" #GroupName, "log_level_" #GroupName, #ConsoleLevel, #LogLevel ); \
|
||||
typedef uint32 __TEmitGroupSanityCheck_##GroupName_##ConsoleLevel_##LogLevel;
|
||||
|
||||
//a utility macro that assists in creating emit groups in a name consistent manner
|
||||
#define DECLARE_GC_EMIT_GROUP( VarName, GroupName ) DECLARE_GC_EMIT_GROUP_DEFAULTS( VarName, GroupName, 3, 3 )
|
||||
|
||||
#endif // GCLOGGER_H
|
||||
239
public/gcsdk/gcmsg.h
Normal file
239
public/gcsdk/gcmsg.h
Normal file
@@ -0,0 +1,239 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: This file defines all of the Game Coordinator messages for the
|
||||
// current IS-embedded implementation of the GC
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCMSG_H
|
||||
#define GCMSG_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "msgbase.h"
|
||||
#include "messagelist.h"
|
||||
|
||||
#pragma pack( push, 1 )
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Header for messages from a client or gameserver to or from the GC
|
||||
//-----------------------------------------------------------------------------
|
||||
struct GCMsgHdr_t
|
||||
{
|
||||
MsgType_t m_eMsg; // The message type
|
||||
uint32 m_nSrcGCDirIndex; // The GC index that this message was sent from (set to the same as the current GC if not routed through another GC)
|
||||
uint64 m_ulSteamID; // User's SteamID
|
||||
|
||||
const char *GetHeaderDescription( );
|
||||
const char *PchMsgName( ) const { return PchMsgNameFromEMsg( m_eMsg ); }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Header for messages from a client or gameserver to or from the GC
|
||||
// That contains source and destination jobs for the purpose of
|
||||
// replying messages.
|
||||
//-----------------------------------------------------------------------------
|
||||
struct GCMsgHdrEx_t
|
||||
{
|
||||
//NOTE: The following fields must be binary equivalent with GCMsgHdr_t above
|
||||
MsgType_t m_eMsg;
|
||||
uint32 m_nSrcGCDirIndex;
|
||||
uint64 m_ulSteamID;
|
||||
// ----
|
||||
|
||||
uint16 m_nHdrVersion;
|
||||
JobID_t m_JobIDTarget;
|
||||
JobID_t m_JobIDSource;
|
||||
|
||||
const char *GetHeaderDescription( );
|
||||
const char *PchMsgName( ) const { return PchMsgNameFromEMsg( m_eMsg ); }
|
||||
};
|
||||
|
||||
|
||||
#pragma pack( push, 1 )
|
||||
struct ProtoBufMsgHeader_t
|
||||
{
|
||||
int32 m_EMsgFlagged; // High bit should be set to indicate this message header type is in use. The rest of the bits indicate message type.
|
||||
uint32 m_cubProtoBufExtHdr; // Size of the extended header which is a serialized protobuf object. Indicates where it ends and the serialized body protobuf begins.
|
||||
|
||||
ProtoBufMsgHeader_t() : m_EMsgFlagged( 0 ), m_cubProtoBufExtHdr( 0 ) {}
|
||||
ProtoBufMsgHeader_t( MsgType_t eMsg, uint32 cubProtoBufExtHdr ) : m_EMsgFlagged( eMsg | k_EMsgProtoBufFlag ), m_cubProtoBufExtHdr( cubProtoBufExtHdr ) {}
|
||||
const char *PchMsgName() const { return PchMsgNameFromEMsg( GetEMsg() ); }
|
||||
MsgType_t GetEMsg() const { return (MsgType_t)(m_EMsgFlagged & (~k_EMsgProtoBufFlag) ); }
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CStructNetPacket
|
||||
// Thin wrapper around raw CNetPacket which implements our IMsgNetPacket interface.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CStructNetPacket : public IMsgNetPacket
|
||||
{
|
||||
#ifdef GC
|
||||
DECLARE_CLASS_MEMPOOL( CStructNetPacket );
|
||||
#endif
|
||||
|
||||
public:
|
||||
CStructNetPacket( CNetPacket *pNetPacket )
|
||||
{
|
||||
m_pHeader = (GCMsgHdrEx_t*)pNetPacket->PubData();
|
||||
m_pNetPacket = pNetPacket;
|
||||
m_pNetPacket->AddRef();
|
||||
}
|
||||
|
||||
EMsgFormatType GetEMsgFormatType() const { return k_EMsgFormatTypeStruct; }
|
||||
CNetPacket *GetCNetPacket() const { return m_pNetPacket; }
|
||||
uint8 *PubData() const { return m_pNetPacket->PubData(); }
|
||||
uint CubData() const { return m_pNetPacket->CubData(); }
|
||||
|
||||
MsgType_t GetEMsg() const { return (MsgType_t)m_pHeader->m_eMsg; }
|
||||
JobID_t GetSourceJobID() const { return m_pHeader->m_JobIDSource; }
|
||||
JobID_t GetTargetJobID() const { return m_pHeader->m_JobIDTarget; }
|
||||
void SetTargetJobID( JobID_t ulJobID ) { m_pHeader->m_JobIDTarget = ulJobID; }
|
||||
|
||||
CSteamID GetSteamID() const { return CSteamID( m_pHeader->m_ulSteamID ); }
|
||||
void SetSteamID( CSteamID steamID ) { m_pHeader->m_ulSteamID = steamID.ConvertToUint64(); }
|
||||
|
||||
AppId_t GetSourceAppID() const { return k_uAppIdInvalid; }
|
||||
void SetSourceAppID( AppId_t appId ) {}
|
||||
|
||||
// Routing to a job name is not permitted with the old packet format
|
||||
virtual bool BHasTargetJobName() const { return false; }
|
||||
virtual const char *GetTargetJobName() const { return NULL; }
|
||||
private:
|
||||
|
||||
virtual ~CStructNetPacket()
|
||||
{
|
||||
m_pNetPacket->Release();
|
||||
}
|
||||
|
||||
CNetPacket *m_pNetPacket;
|
||||
GCMsgHdrEx_t *m_pHeader;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#define GCMSG_EX_HEADER_SIZE ( sizeof( GCSDK::GCMsgHdrEx_t ) - sizeof( GCSDK::GCMsgHdr_t ) )
|
||||
|
||||
static const uint16 k_nHdrVersion = 0x1;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Header for messages from one GC to another
|
||||
//-----------------------------------------------------------------------------
|
||||
struct GCMsgInterHdr_t
|
||||
{
|
||||
MsgType_t m_eMsg; // The message type
|
||||
uint32 m_unSourceAppId; // App ID of the source GC
|
||||
uint16 m_nHdrVersion;
|
||||
JobID_t m_JobIDTarget;
|
||||
JobID_t m_JobIDSource;
|
||||
|
||||
const char *GetHeaderDescription( );
|
||||
const char *PchMsgName( ) const { return PchMsgNameFromEMsg( m_eMsg ); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
#pragma pack( pop )
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CGCMsgBase
|
||||
// Message class for messages between the GC and a GS or client
|
||||
// Handles a message with a header of type GCMsgHdrEx_t, a payload of type MSG_BODY_TYPE, and optional variable length data
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef CMsgBase_t<GCMsgHdrEx_t> CGCMsgBase;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template <typename MSG_BODY_TYPE>
|
||||
class CGCMsg : public CGCMsgBase
|
||||
{
|
||||
public:
|
||||
// Client send constructor
|
||||
CGCMsg( MsgType_t eMsg, uint32 cubReserve = 64 ) :
|
||||
CGCMsgBase( sizeof( MSG_BODY_TYPE ), cubReserve )
|
||||
{
|
||||
// Fill out the message header
|
||||
Hdr().m_eMsg = eMsg;
|
||||
Hdr().m_nHdrVersion = k_nHdrVersion;
|
||||
Hdr().m_JobIDSource = k_GIDNil;
|
||||
Hdr().m_JobIDTarget = k_GIDNil;
|
||||
}
|
||||
|
||||
// reply constructor
|
||||
CGCMsg( MsgType_t eMsg, const CGCMsgBase &msg, uint32 cubReserve = 64 ) :
|
||||
CGCMsgBase( sizeof( MSG_BODY_TYPE ), cubReserve )
|
||||
{
|
||||
// Fill out the message header
|
||||
Hdr().m_eMsg = eMsg;
|
||||
Hdr().m_ulSteamID = msg.Hdr().m_ulSteamID;
|
||||
Hdr().m_nHdrVersion = k_nHdrVersion;
|
||||
Hdr().m_JobIDSource = k_GIDNil;
|
||||
Hdr().m_JobIDTarget = msg.Hdr().m_JobIDSource;
|
||||
}
|
||||
|
||||
CGCMsg( uint8 *pubPkt, uint32 cubPkt ) :
|
||||
CGCMsgBase( sizeof( GCMsgHdrEx_t ), sizeof( MSG_BODY_TYPE ), pubPkt, cubPkt )
|
||||
{
|
||||
}
|
||||
|
||||
// Receive constructor
|
||||
// Use this constructor when creating a message from a received network packet
|
||||
CGCMsg( CIMsgNetPacketAutoRelease &refNetPacket )
|
||||
:CGCMsgBase( sizeof( GCMsgHdrEx_t ), sizeof( MSG_BODY_TYPE ), refNetPacket->PubData(), refNetPacket->CubData() )
|
||||
{
|
||||
}
|
||||
|
||||
// Receive constructor
|
||||
// Use this constructor when creating a message from a received network packet
|
||||
CGCMsg( IMsgNetPacket *pNetPacket )
|
||||
:CGCMsgBase( sizeof( GCMsgHdrEx_t ), sizeof( MSG_BODY_TYPE ), pNetPacket->PubData(), pNetPacket->CubData() )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// Receive constructor
|
||||
// Use this constructor when creating a message from a received network packet
|
||||
CGCMsg( CNetPacket *pNetPacket )
|
||||
:CGCMsgBase( sizeof( GCMsgHdrEx_t ), sizeof( MSG_BODY_TYPE ), pNetPacket->PubData(), pNetPacket->CubData() )
|
||||
{
|
||||
}
|
||||
|
||||
// empty constructor
|
||||
CGCMsg() :
|
||||
CGCMsgBase( sizeof( GCMsgHdrEx_t ), sizeof( MSG_BODY_TYPE ), NULL, 0 )
|
||||
{
|
||||
}
|
||||
|
||||
~CGCMsg()
|
||||
{
|
||||
}
|
||||
|
||||
// Accessors
|
||||
MSG_BODY_TYPE &Body() { return * ( MSG_BODY_TYPE * ) ( m_pubPkt + m_cubMsgHdr ); }
|
||||
const MSG_BODY_TYPE &Body() const { return * ( MSG_BODY_TYPE * ) ( m_pubPkt + m_cubMsgHdr ); }
|
||||
GCMsgHdrEx_t * PGCMsgHdr() { return ( ( GCMsgHdrEx_t * ) m_pubPkt ); }
|
||||
MsgType_t GetEMsg() const { return Hdr().m_eMsg; }
|
||||
|
||||
// Called to set the JobID that will be expecting
|
||||
// a reply to this message.
|
||||
void ExpectingReply( JobID_t jobIDSource )
|
||||
{
|
||||
Hdr().m_JobIDSource = jobIDSource;
|
||||
}
|
||||
|
||||
bool BIsExpectingReply() { return Hdr().m_JobIDSource != k_GIDNil; }
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCMSG_H
|
||||
50
public/gcsdk/gcschema.h
Normal file
50
public/gcsdk/gcschema.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//========= Copyright ©, Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef GC_SCHEMA_H
|
||||
#define GC_SCHEMA_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/platform.h"
|
||||
#include "steam/steamtypes.h"
|
||||
|
||||
#include "tier0/dbg.h"
|
||||
|
||||
// include this before checksum_crc specifically to avoid the
|
||||
// CRC references
|
||||
#include "steam/steamclientpublic.h"
|
||||
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier1/utlstring.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
#include "tier1/mempool.h"
|
||||
#include "tier1/tsmempool.h"
|
||||
#include "tier1/tsmultimempool.h"
|
||||
#include "tier1/fmtstr.h"
|
||||
|
||||
#include "vstdlib/coroutine.h"
|
||||
|
||||
// public stuff
|
||||
#include "gamecoordinator/igcsqlresultsetlist.h"
|
||||
|
||||
// These are first since they're used all over
|
||||
#include "gcconstants.h"
|
||||
#include "refcount.h"
|
||||
|
||||
// SQL Access stuff
|
||||
#include "sqlaccess/record.h"
|
||||
#include "sqlaccess/schema.h"
|
||||
|
||||
#include "sqlaccess/recordinfo.h"
|
||||
#include "sqlaccess/schemafull.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
107
public/gcsdk/gcsdk.h
Normal file
107
public/gcsdk/gcsdk.h
Normal file
@@ -0,0 +1,107 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: includes all the headers required for the GC SDK. Include this
|
||||
// in your stdafx.h
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSDK_H
|
||||
#define GCSDK_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#pragma once
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_BitScanReverse)
|
||||
#endif
|
||||
|
||||
#include "tier0/platform.h"
|
||||
|
||||
#include "gcsteamdefines.h"
|
||||
|
||||
#include "steam/steamtypes.h"
|
||||
|
||||
#include "tier0/dbg.h"
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
#include "tier0/validator.h"
|
||||
#endif
|
||||
#include "tier0/vprof.h"
|
||||
#include "tier0/fasttimer.h"
|
||||
|
||||
// include this before checksum_crc specifically to avoid the
|
||||
// CRC references
|
||||
#include "steam/steamclientpublic.h"
|
||||
|
||||
#include "tier1/utlmap.h"
|
||||
#include "tier1/utllinkedlist.h"
|
||||
#include "tier1/utlpriorityqueue.h"
|
||||
#include "tier1/utlstring.h"
|
||||
#include "tier1/utlsymbollarge.h"
|
||||
#include "tier1/utlbuffer.h"
|
||||
#include "tier1/utldict.h"
|
||||
#include "tier1/utlhashmaplarge.h"
|
||||
#include "tier1/mempool.h"
|
||||
#include "tier1/tsmempool.h"
|
||||
#include "tier1/tsmultimempool.h"
|
||||
#include "tier1/checksum_crc.h"
|
||||
#include "tier1/fmtstr.h"
|
||||
#include "tier1/keyvalues.h"
|
||||
#include "tier1/strtools.h"
|
||||
#include "tier1/utlsymbol.h"
|
||||
#include "tier1/utlsymbollarge.h"
|
||||
|
||||
#include "vstdlib/coroutine.h"
|
||||
#include "vstdlib/osversion.h"
|
||||
|
||||
// public stuff
|
||||
#include "gamecoordinator/igcsqlresultsetlist.h"
|
||||
#include "misc.h"
|
||||
|
||||
// These are first since they're used all over
|
||||
#include "gcconstants.h"
|
||||
#include "refcount.h"
|
||||
#include "string_misc.h"
|
||||
#include "netpacket.h"
|
||||
#include "gcmsg.h"
|
||||
#include "msgprotobuf.h"
|
||||
#include "gc_convar.h"
|
||||
|
||||
// SQL Access stuff
|
||||
#include "sqlaccess/record.h"
|
||||
#include "sqlaccess/schema.h"
|
||||
#include "sqlaccess/recordinfo.h"
|
||||
#include "sqlaccess/schemafull.h"
|
||||
#include "sqlaccess/columnset.h"
|
||||
#include "sqlaccess/sqlrecord.h"
|
||||
#include "sqlaccess/sqlutil.h"
|
||||
#include "sqlaccess/sqlaccess.h"
|
||||
|
||||
#include "messagelist.h"
|
||||
#include "gchost.h"
|
||||
#include "gclogger.h"
|
||||
#include "gcsqlquery.h"
|
||||
#include "jobtime.h"
|
||||
#include "job.h"
|
||||
#include "jobmgr.h"
|
||||
#include "netpacketpool.h"
|
||||
#include "gcsystemmsgs.h"
|
||||
#include "gcwgjobmgr.h"
|
||||
#include "gcbase.h"
|
||||
#include "gcsession.h"
|
||||
#include "sharedobject.h"
|
||||
#include "protobufsharedobject.h"
|
||||
#include "schemasharedobject.h"
|
||||
#include "sharedobjectcache.h"
|
||||
#include "gcdirtyfield.h"
|
||||
#include "gc_sharedobjectcache.h"
|
||||
#include "http.h"
|
||||
#include "gcwebapi.h"
|
||||
#include "gcwebapikey.h"
|
||||
#include "webapi_response.h"
|
||||
#include "gcjob.h"
|
||||
#include "msgprotobuf.h"
|
||||
#include "sdocache.h"
|
||||
|
||||
#endif // GCSDK_H
|
||||
7
public/gcsdk/gcsdk_auto.h
Normal file
7
public/gcsdk/gcsdk_auto.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// automatically include the right GCSDK header to get
|
||||
// appropriate stuff included from other GCSDK headers
|
||||
#ifdef GC
|
||||
#include "gcsdk.h"
|
||||
#else
|
||||
#include "gcclientsdk.h"
|
||||
#endif
|
||||
143
public/gcsdk/gcsession.h
Normal file
143
public/gcsdk/gcsession.h
Normal file
@@ -0,0 +1,143 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Holds the CGCSession class
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSESSION_H
|
||||
#define GCSESSION_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CGCGSSession;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Base class for sessions in the GC
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCSession
|
||||
{
|
||||
public:
|
||||
CGCSession( const CSteamID & steamID, CGCSharedObjectCache *pCache );
|
||||
virtual ~CGCSession();
|
||||
|
||||
const CSteamID & GetSteamID() const { return m_steamID; }
|
||||
|
||||
const CGCSharedObjectCache *GetSOCache() const { return m_pSOCache; }
|
||||
CGCSharedObjectCache *GetSOCache() { return m_pSOCache; }
|
||||
void RemoveSOCache() { m_pSOCache = NULL; }
|
||||
|
||||
EOSType GetOSType() const;
|
||||
bool IsTestSession() const;
|
||||
uint32 GetIPPublic() const;
|
||||
|
||||
bool BIsShuttingDown() const { return m_bIsShuttingDown; }
|
||||
void SetIsShuttingDown( bool bIsShuttingDown ) { m_bIsShuttingDown = bIsShuttingDown; }
|
||||
|
||||
virtual void Dump( bool bFull = true ) const = 0;
|
||||
|
||||
virtual void MarkAccess() { }
|
||||
virtual void Run();
|
||||
virtual void YieldingSOCacheReloaded() {}
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName );
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
private:
|
||||
CSteamID m_steamID;
|
||||
CGCSharedObjectCache *m_pSOCache;
|
||||
uint32 m_unIPPublic;
|
||||
EOSType m_osType : 16;
|
||||
bool m_bIsShuttingDown : 1;
|
||||
bool m_bIsTestSession : 1;
|
||||
|
||||
friend class CGCBase;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Base class for user sessions in the GC
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCUserSession : public CGCSession
|
||||
{
|
||||
public:
|
||||
CGCUserSession( const CSteamID & steamID, CGCSharedObjectCache *pCache ) : CGCSession( steamID, pCache ) { }
|
||||
virtual ~CGCUserSession();
|
||||
|
||||
virtual bool BInit();
|
||||
|
||||
const CSteamID &GetSteamIDGS() const { return m_steamIDGS; }
|
||||
const CSteamID &GetSteamIDGSPrev() const { return m_steamIDGSPrev; }
|
||||
|
||||
virtual bool BSetServer( const CSteamID &steamIDGS );
|
||||
virtual bool BLeaveServer();
|
||||
virtual void Dump( bool bFull = true ) const;
|
||||
|
||||
private:
|
||||
CSteamID m_steamIDGS;
|
||||
CSteamID m_steamIDGSPrev;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Base class for gameserver sessions in the GC
|
||||
//-----------------------------------------------------------------------------
|
||||
class CGCGSSession : public CGCSession
|
||||
{
|
||||
public:
|
||||
CGCGSSession( const CSteamID & steamID, CGCSharedObjectCache *pCache, uint32 unServerAddr, uint16 usServerPort ) ;
|
||||
virtual ~CGCGSSession();
|
||||
|
||||
uint32 GetAddr() const { return m_unServerAddr; }
|
||||
uint16 GetPort() const { return m_usServerPort; }
|
||||
void SetIPAndPort( uint32 unServerAddr, uint16 usServerPort ) { m_unServerAddr = unServerAddr; m_usServerPort = usServerPort; }
|
||||
|
||||
int GetUserCount() const { return m_vecUsers.Count(); }
|
||||
const CSteamID &GetUserID( int nIndex ) const { return m_vecUsers[nIndex]; }
|
||||
|
||||
// Manages users on the server. It is very important that these are not
|
||||
// virtual and not yielding. For custom behavior override the Pre*() hooks below
|
||||
bool BAddUser( const CSteamID &steamIDUser );
|
||||
bool BRemoveUser( const CSteamID &steamIDUser );
|
||||
void RemoveAllUsers();
|
||||
|
||||
virtual void Dump( bool bFull = true ) const;
|
||||
|
||||
protected:
|
||||
// Hooks to trigger custom behavior when users are added and removed. It is
|
||||
// very important that these do not yield. If you need to yield, start a job instead
|
||||
virtual void PreAddUser( const CSteamID &steamIDUser ) {}
|
||||
virtual void PostAddUser( const CSteamID &steamIDUser ) {}
|
||||
virtual void PreRemoveUser( const CSteamID &steamIDUser ) {}
|
||||
virtual void PostRemoveUser( const CSteamID &steamIDUser ) {}
|
||||
virtual void PreRemoveAllUsers() {}
|
||||
virtual void PostRemoveAllUsers() {}
|
||||
|
||||
public:
|
||||
// These are the addresses the server itself told us
|
||||
uint32 m_unServerPublicIPAddr;
|
||||
uint32 m_unServerPrivateIPAddr;
|
||||
uint32 m_unServerPort;
|
||||
uint16 m_unServerTVPort;
|
||||
CUtlString m_serverKey;
|
||||
bool m_bServerHibernating;
|
||||
int m_serverType;
|
||||
int m_region;
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName );
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
protected:
|
||||
CUtlVector<CSteamID> m_vecUsers;
|
||||
|
||||
// These are the address of the server as connected to Steam
|
||||
uint32 m_unServerAddr;
|
||||
uint16 m_usServerPort;
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCSESSION_H
|
||||
151
public/gcsdk/gcsqlquery.h
Normal file
151
public/gcsdk/gcsqlquery.h
Normal file
@@ -0,0 +1,151 @@
|
||||
//========= Copyright ©, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSQLQUERY_H
|
||||
#define GCSQLQUERY_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gamecoordinator/igcsqlquery.h"
|
||||
#include "refcount.h"
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
struct GCSQLBindParam_t
|
||||
{
|
||||
EGCSQLType m_eType;
|
||||
uint8 *m_pubData;
|
||||
size_t m_cubData;
|
||||
};
|
||||
|
||||
|
||||
class CGCSQLQuery
|
||||
{
|
||||
public:
|
||||
CGCSQLQuery();
|
||||
virtual ~CGCSQLQuery();
|
||||
|
||||
void SetCommand( const char *pchCommand ) { m_sCommand = pchCommand; }
|
||||
|
||||
void AddBindParam( const char *pchValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_String, (byte *)pchValue, Q_strlen( pchValue ) );
|
||||
}
|
||||
|
||||
void AddBindParam( const int16 nValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_int16, (byte *)&nValue, sizeof( nValue ) );
|
||||
}
|
||||
|
||||
void AddBindParam( const uint16 uValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_int16, (byte *)&uValue, sizeof( uValue ) );
|
||||
}
|
||||
|
||||
void AddBindParam( const int32 nValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_int32, (byte *)&nValue, sizeof( nValue ) );
|
||||
}
|
||||
|
||||
void AddBindParam( const uint32 uValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_int32, (byte *)&uValue, sizeof( uValue ) );
|
||||
}
|
||||
|
||||
void AddBindParam( const uint64 ulValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_int64, (byte *)&ulValue, sizeof( ulValue ) );
|
||||
}
|
||||
|
||||
void AddBindParam( const uint8 *ubValue, const int cubValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_Blob, (byte *)ubValue, cubValue );
|
||||
}
|
||||
|
||||
void AddBindParam( const float fValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_float, (byte *)&fValue, sizeof ( fValue ) );
|
||||
}
|
||||
|
||||
void AddBindParam( const double dValue )
|
||||
{
|
||||
AddBindParamRaw( k_EGCSQLType_double, (byte *)&dValue, sizeof ( dValue ) );
|
||||
}
|
||||
|
||||
void ClearParams();
|
||||
|
||||
// this is used internally to bind a field with its type. You probably want
|
||||
// some version of AddBindParam instead of this.
|
||||
void AddBindParamRaw( EGCSQLType eType, const byte *pubData, uint32 cubData );
|
||||
|
||||
// ------- Interface implementation from IGCSQLQuery -----
|
||||
|
||||
// get the null-terminated query string itself
|
||||
virtual const char *PchCommand() { return m_sCommand.Get(); }
|
||||
|
||||
// gets the parameter data
|
||||
virtual uint32 CnParams() { return m_vecParams.Count(); }
|
||||
virtual EGCSQLType EParamType( uint32 uIndex ) { return m_vecParams[uIndex].m_eType; }
|
||||
virtual byte *PubParam( uint32 uIndex ) { return m_vecParams[uIndex].m_pubData; }
|
||||
virtual uint32 CubParam( uint32 uIndex ) { return m_vecParams[uIndex].m_cubData; }
|
||||
|
||||
private:
|
||||
|
||||
CUtlString m_sCommand;
|
||||
CUtlVector< GCSQLBindParam_t > m_vecParams;
|
||||
};
|
||||
|
||||
class CGCSQLQueryGroup : public IGCSQLQuery, public CRefCount
|
||||
{
|
||||
// create query groups on the heap with alloc
|
||||
CGCSQLQueryGroup();
|
||||
|
||||
// destroy query groups by releasing them
|
||||
virtual ~CGCSQLQueryGroup();
|
||||
public:
|
||||
static CGCSQLQueryGroup *Alloc() { return new CGCSQLQueryGroup(); }
|
||||
|
||||
void AddQuery( CGCSQLQuery *pQuery );
|
||||
void SetName( const char *sName );
|
||||
|
||||
// returns the number of statements in the transaction
|
||||
// represented by this query object
|
||||
virtual uint32 GetStatementCount() { return m_vecQueries.Count(); }
|
||||
|
||||
// returns a string that represents where in the GC this
|
||||
// query came from. Usually this is FILE_AND_LINE.
|
||||
virtual const char *PchName() { return m_sName; }
|
||||
|
||||
// get the null-terminated query string itself
|
||||
virtual const char *PchCommand( uint32 unStatement ) { return m_vecQueries[unStatement]->PchCommand(); }
|
||||
|
||||
// gets the parameter data
|
||||
virtual uint32 CnParams( uint32 unStatement ) { return m_vecQueries[unStatement]->CnParams(); }
|
||||
virtual EGCSQLType EParamType( uint32 unStatement, uint32 uIndex ) { return m_vecQueries[unStatement]->EParamType( uIndex ); }
|
||||
virtual byte *PubParam( uint32 unStatement, uint32 uIndex ) { return m_vecQueries[unStatement]->PubParam( uIndex ); }
|
||||
virtual uint32 CubParam( uint32 unStatement, uint32 uIndex ) { return m_vecQueries[unStatement]->CubParam( uIndex ); };
|
||||
|
||||
// reports the result
|
||||
virtual void SetResults( IGCSQLResultSetList *pResults );
|
||||
IGCSQLResultSetList *GetResults() { return m_pResults; }
|
||||
|
||||
// clears all the queries in the query group and resets its name
|
||||
void Clear();
|
||||
private:
|
||||
CUtlVector< CGCSQLQuery * > m_vecQueries;
|
||||
CUtlString m_sName;
|
||||
IGCSQLResultSetList *m_pResults;
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // GCSQLQUERY_H
|
||||
54
public/gcsdk/gcsteamdefines.h
Normal file
54
public/gcsdk/gcsteamdefines.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Defines a bunch of stuff that would be defined in Steam, but
|
||||
// isn't in Source.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSTEAMDEFINES_H
|
||||
#define GCSTEAMDEFINES_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/memalloc.h"
|
||||
|
||||
// steam defines some things that games don't
|
||||
#ifndef STEAM
|
||||
#define PvAlloc(x) malloc(x)
|
||||
#define PvRealloc(x, y) realloc(x, y)
|
||||
#define FreePv(x) free(x)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DbgVerify(x) Assert(x)
|
||||
#else
|
||||
#define DbgVerify(x) (x)
|
||||
#endif
|
||||
|
||||
// auto-lock class for read-write locks
|
||||
template< class T >
|
||||
class CRWLockAutoWrite
|
||||
{
|
||||
T &m_RWLock;
|
||||
public:
|
||||
CRWLockAutoWrite( T &RWLock ) : m_RWLock( RWLock )
|
||||
{
|
||||
m_RWLock.LockForWrite();
|
||||
}
|
||||
|
||||
~CRWLockAutoWrite()
|
||||
{
|
||||
m_RWLock.UnlockWrite();
|
||||
}
|
||||
};
|
||||
|
||||
#define AUTO_LOCK_WRITE( mutex ) CRWLockAutoWrite<CThreadRWLock> UNIQUE_ID( mutex )
|
||||
#define AUTO_LOCK_SPIN_WRITE( mutex ) CRWLockAutoWrite<CThreadSpinRWLock> UNIQUE_ID( mutex )
|
||||
|
||||
//inline void *MemAlloc_AllocAligned( size_t size, size_t align, bool bCanFail ) { return MemAlloc_AllocAligned( size, align ); }
|
||||
inline void MemAlloc_FreeAligned( void *pMemBlock, bool bOperatorNew ) { MemAlloc_FreeAligned( pMemBlock ); }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GCSTEAMDEFINES_H
|
||||
291
public/gcsdk/gcsystemmsgs.h
Normal file
291
public/gcsdk/gcsystemmsgs.h
Normal file
@@ -0,0 +1,291 @@
|
||||
//====== Copyright (C), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: This file defines all of our over-the-wire net protocols for the
|
||||
// global system messages used by the GC. These are usually sent by
|
||||
// the GC Host so be very careful of versioning issues when you consider
|
||||
// changing them. Note that we never use types with undefined length
|
||||
// (like int). Always use an explicit type (like int32).
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSYSTEMMSGS_H
|
||||
#define GCSYSTEMMSGS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
|
||||
#pragma pack( push, 8 ) // this is a 8 instead of a 1 to maintain backward compatibility with Steam
|
||||
|
||||
enum EGCSystemMsg
|
||||
{
|
||||
k_EGCMsgInvalid = 0,
|
||||
k_EGCMsgMulti = 1,
|
||||
|
||||
k_EGCMsgGenericReply = 10,
|
||||
|
||||
k_EGCMsgSystemBase = 50,
|
||||
k_EGCMsgAchievementAwarded = 51,
|
||||
k_EGCMsgConCommand = 52, // A command from the GC's admin console
|
||||
k_EGCMsgStartPlaying = 53,
|
||||
k_EGCMsgStopPlaying = 54,
|
||||
k_EGCMsgStartGameserver = 55,
|
||||
k_EGCMsgStopGameserver = 56,
|
||||
k_EGCMsgWGRequest = 57,
|
||||
k_EGCMsgWGResponse = 58,
|
||||
k_EGCMsgGetUserGameStatsSchema = 59, // Gets the user game stats schema for the app
|
||||
k_EGCMsgGetUserGameStatsSchemaResponse = 60,
|
||||
k_EGCMsgGetUserStatsDEPRECATED = 61, // Gets user game stats for a user
|
||||
k_EGCMsgGetUserStatsResponse = 62,
|
||||
k_EGCMsgAppInfoUpdated = 63, // Message sent to the GC when there has been an AppInfo update
|
||||
k_EGCMsgValidateSession = 64, // Message sent by the GC when it wants to make sure a session exists
|
||||
k_EGCMsgValidateSessionResponse = 65, // Message sent to the GC in response to ValidateSession
|
||||
k_EGCMsgLookupAccountFromInput = 66, // Sent by the GC to lookup user. Reply is k_EGCMsgGenericReply
|
||||
k_EGCMsgSendHTTPRequest = 67, // Message sent by the GC to do a generic HTTP request
|
||||
k_EGCMsgSendHTTPRequestResponse = 68, // Response back to the GC with the results of the HTTP request
|
||||
k_EGCMsgPreTestSetup = 69, // Reset the GC database (usually for testing purposes)
|
||||
k_EGCMsgRecordSupportAction = 70, // Logs a support action
|
||||
k_EGCMsgGetAccountDetails = 71, // Requests the details for an account
|
||||
k_EGCMsgSendInterAppMessage = 72, // Sends a message to another app's GC
|
||||
k_EGCMsgReceiveInterAppMessage = 73, // Receives a message from another app's GC
|
||||
k_EGCMsgFindAccounts = 74, // queries the AMs for accounts by name
|
||||
k_EGCMsgPostAlert = 75, // posts an alert to Steam
|
||||
k_EGCMsgGetLicenses = 76, // asks Steam for the user's licenses
|
||||
k_EGCMsgGetUserStats = 77, // Gets user game stats for a user
|
||||
k_EGCMsgGetCommands = 78, // request for a list of commands from a gc console
|
||||
k_EGCMsgGetCommandsResponse = 79, // response with a list of commands for a gc console
|
||||
k_EGCMsgAddFreeLicense = 80, // request for for Steam to add a license to the specified free package
|
||||
k_EGCMsgAddFreeLicenseResponse = 81, // response with the result of the attempt to add a free license
|
||||
k_EGCMsgGetIPLocation = 82, // Get geolocation data for a specific IP
|
||||
k_EGCMsgGetIPLocationResponse = 83, // Geolocation response
|
||||
|
||||
k_EGCMsgSystemStatsSchema = 84, // Message sent by the GC specifying what its stats schema is
|
||||
k_EGCMsgGetSystemStats = 85, // Message sent to the GC requesting its stats
|
||||
k_EGCMsgGetSystemStatsResponse = 86, // Message sent by the GC with its stats
|
||||
|
||||
k_EGCMsgSendEmail = 87, // Sent by the GC to send an email to a user
|
||||
k_EGCMsgSendEmailResponse = 88, // Response with the result of the send request
|
||||
k_EGCMsgGetEmailTemplate = 89, // Sent to the GC to request an email template
|
||||
k_EGCMsgGetEmailTemplateResponse = 90, // Get email template response
|
||||
|
||||
// web API calls
|
||||
k_EGCMsgWebAPIBase = 100,
|
||||
k_EGCMsgWebAPIRegisterInterfaces = k_EGCMsgWebAPIBase + 1, // sent once at startup to register APIs
|
||||
k_EGCMsgWebAPIJobRequest = k_EGCMsgWebAPIBase + 2, // sent when an actual request is made
|
||||
k_EGCMsgWebAPIRegistrationRequested = k_EGCMsgWebAPIBase + 3, // sent by the GC Host when it learns a web API server has started
|
||||
|
||||
// Memcached
|
||||
k_EGCMsgMemCachedBase = 200, // Get key(s) from memcached
|
||||
k_EGCMsgMemCachedGet = k_EGCMsgMemCachedBase, // Get key(s) from memcached
|
||||
k_EGCMsgMemCachedGetResponse = k_EGCMsgMemCachedBase + 1, // Retrieved keys
|
||||
k_EGCMsgMemCachedSet = k_EGCMsgMemCachedBase + 2, // Set key(s) into memcached
|
||||
k_EGCMsgMemCachedDelete = k_EGCMsgMemCachedBase + 3, // Delete key(s) from memcached
|
||||
};
|
||||
|
||||
|
||||
// generic zero-length message struct
|
||||
struct MsgGCEmpty_t
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
// k_EGCMsgAchievementAwarded
|
||||
struct MsgGCAchievementAwarded_t
|
||||
{
|
||||
uint16 m_usStatID;
|
||||
uint8 m_ubBit;
|
||||
// var data:
|
||||
// string data: name of achievement earned
|
||||
};
|
||||
|
||||
// k_EGCMsgConCommand
|
||||
struct MsgGCConCommand_t
|
||||
{
|
||||
// var data:
|
||||
// string: the command as typed into the console
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgStartPlaying
|
||||
struct MsgGCStartPlaying_t
|
||||
{
|
||||
CSteamID m_steamID;
|
||||
CSteamID m_steamIDGS;
|
||||
uint32 m_unServerAddr;
|
||||
uint16 m_usServerPort;
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgStartPlaying
|
||||
// k_EGCMsgStopGameserver
|
||||
struct MsgGCStopSession_t
|
||||
{
|
||||
CSteamID m_steamID;
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgStartGameserver
|
||||
struct MsgGCStartGameserver_t
|
||||
{
|
||||
CSteamID m_steamID;
|
||||
uint32 m_unServerAddr;
|
||||
uint16 m_usServerPort;
|
||||
};
|
||||
|
||||
// k_EGCMsgWGRequest
|
||||
struct MsgGCWGRequest_t
|
||||
{
|
||||
uint64 m_ulSteamID; //SteamID of auth'd WG user
|
||||
uint32 m_unPrivilege; // The EGCWebApiPrivilege value that the request was made with
|
||||
uint32 m_cubKeyValues; // length of the key values data blob in message (starts after string request name data)
|
||||
// var data -
|
||||
// request name
|
||||
// binary key values of web request
|
||||
};
|
||||
|
||||
// k_EGCMsgWGResponse
|
||||
struct MsgGCWGResponse_t
|
||||
{
|
||||
bool m_bResult; // True if the request was successful
|
||||
uint32 m_cubKeyValues; // length of the key values data blob in message
|
||||
// var data -
|
||||
// binary key values of web response
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgGetUserGameStatsSchemaResponse
|
||||
struct MsgGetUserGameStatsSchemaResponse_t
|
||||
{
|
||||
bool m_bSuccess; // True is the request was successful
|
||||
// var data -
|
||||
// binary key values containing the User Game Stats schema
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgGetUserStats
|
||||
struct MsgGetUserStats_t
|
||||
{
|
||||
uint64 m_ulSteamID; // SteamID the stats are requested for
|
||||
uint16 m_cStatIDs; // A count of the number of statIDs requested
|
||||
// var data -
|
||||
// Array of m_cStatIDs 16-bit StatIDs
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgGetUserStatsResponse
|
||||
struct MsgGetUserStatsResponse_t
|
||||
{
|
||||
uint64 m_ulSteamID; // SteamID the stats were requested for
|
||||
bool m_bSuccess; // True is the request was successful
|
||||
uint16 m_cStats; // Number of stats returned in the message
|
||||
// var data -
|
||||
// m_cStats instances of:
|
||||
// uint16 usStatID - Stat ID
|
||||
// uint32 unData - Stat value
|
||||
};
|
||||
|
||||
// k_EGCMsgValidateSession
|
||||
struct MsgGCValidateSession_t
|
||||
{
|
||||
uint64 m_ulSteamID; // SteamID to validate
|
||||
};
|
||||
|
||||
// k_EGCMsgValidateSessionResponse
|
||||
struct MsgGCValidateSessionResponse_t
|
||||
{
|
||||
uint64 m_ulSteamID;
|
||||
uint64 m_ulSteamIDGS;
|
||||
uint32 m_unServerAddr;
|
||||
uint16 m_usServerPort;
|
||||
bool m_bOnline;
|
||||
};
|
||||
|
||||
// response to k_EGCMsgLookupAccountFromInput
|
||||
struct MsgGCLookupAccountResponse
|
||||
{
|
||||
uint64 m_ulSteamID;
|
||||
};
|
||||
|
||||
// k_EGCMsgSendHTTPRequest
|
||||
struct MsgGCSendHTTPRequest_t
|
||||
{
|
||||
// Variable data:
|
||||
// - Serialized CHTTPRequest
|
||||
};
|
||||
|
||||
// k_EGCMsgSendHTTPRequestResponse
|
||||
struct MsgGCSendHTTPRequestResponse_t
|
||||
{
|
||||
bool m_bCompleted;
|
||||
// Variable data:
|
||||
// - if m_bCompleted is true, Serialized CHTTPResponse
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgRecordSupportAction
|
||||
struct MsgGCRecordSupportAction_t
|
||||
{
|
||||
uint32 m_unAccountID; // which account is affected (object)
|
||||
uint32 m_unActorID; // who made the change (subject)
|
||||
// Variable data:
|
||||
// - string - Custom data for the event
|
||||
// - string - A note with the reason for the change
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgWebAPIRegisterInterfaces
|
||||
struct MsgGCWebAPIRegisterInterfaces_t
|
||||
{
|
||||
uint32 m_cInterfaces;
|
||||
// Variable data:
|
||||
// - KeyValues for interface - one per interface
|
||||
};
|
||||
|
||||
// k_EGCMsgGetAccountDetails
|
||||
struct MsgGCGetAccountDetails_t
|
||||
{
|
||||
uint64 m_ulSteamID; // SteamID to validate
|
||||
};
|
||||
|
||||
|
||||
// k_EGCMsgSendInterAppMessage
|
||||
// k_EGCMsgReceiveInterAppMessage
|
||||
struct MsgGCInterAppMessage_t
|
||||
{
|
||||
AppId_t m_unAppID;
|
||||
|
||||
// Variable data:
|
||||
// The message to send. Both GCs need to agree on the protocol
|
||||
};
|
||||
|
||||
|
||||
// Used by k_EGCMsgFindAccounts
|
||||
enum EAccountFindType
|
||||
{
|
||||
k_EFindAccountTypeInvalid = 0,
|
||||
k_EFindAccountTypeAccountName = 1,
|
||||
k_EFindAccountTypeEmail,
|
||||
k_EFindAccountTypePersonaName,
|
||||
k_EFindAccountTypeURL,
|
||||
k_EFindAccountTypeAllOnline,
|
||||
k_EFindAccountTypeAll,
|
||||
k_EFindClanTypeClanName,
|
||||
k_EFindClanTypeURL,
|
||||
k_EFindClanTypeOfficialURL,
|
||||
k_EFindClanTypeAppID,
|
||||
};
|
||||
|
||||
|
||||
extern void InitGCSystemMessageTypes();
|
||||
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#pragma pack( pop )
|
||||
|
||||
#endif // GCSYSTEMMSGS_H
|
||||
103
public/gcsdk/gcwebapi.h
Normal file
103
public/gcsdk/gcwebapi.h
Normal file
@@ -0,0 +1,103 @@
|
||||
//========= Copyright ©, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: CWAPI header for GC access to the Web API server
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCWEBAPI_H
|
||||
#define GCWEBAPI_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
enum EWebAPIPrivilege
|
||||
{
|
||||
k_EWebApiPriv_Invalid = -1,
|
||||
k_EWebApiPriv_None = 0, // fully public, no auth needed
|
||||
k_EWebApiPriv_Key = 1, // Requires valid key
|
||||
k_EWebApiPriv_PublisherKey = 2, // Requires publisher key
|
||||
k_EWebApiPriv_PublisherKeyOwnsApp = 3, // Requires publisher key and publisher owns appid
|
||||
//k_EWebApiPriv_Account = 1, // user must have a Steam account with password set
|
||||
};
|
||||
|
||||
enum EWebApiParamType
|
||||
{
|
||||
k_EWebApiParamTypeInvalid = -1,
|
||||
|
||||
k_EWebApiParamTypeInt32 = 0,
|
||||
k_EWebApiParamTypeUInt32 = 1,
|
||||
k_EWebApiParamTypeInt64 = 2,
|
||||
k_EWebApiParamTypeUInt64 = 3,
|
||||
k_EWebApiParamTypeFloat = 4,
|
||||
k_EWebApiParamTypeString = 5,
|
||||
k_EWebApiParamTypeBool = 6,
|
||||
k_EWebApiParamTypeRawBinary = 7,
|
||||
};
|
||||
|
||||
const char *PchNameFromEWebApiParamType( int eWebApiParamType );
|
||||
|
||||
typedef KeyValues *(*GCWebAPIInterfaceMapCreationFunc_t)();
|
||||
|
||||
class CGCWebAPIInterfaceMapRegistrar
|
||||
{
|
||||
public:
|
||||
CGCWebAPIInterfaceMapRegistrar( GCWebAPIInterfaceMapCreationFunc_t pFunc )
|
||||
{
|
||||
VecInstance().AddToTail( pFunc );
|
||||
}
|
||||
|
||||
static CUtlVector< GCWebAPIInterfaceMapCreationFunc_t > & VecInstance();
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Macros for use registering interfaces in webapi_interfacemap.h
|
||||
//
|
||||
#define BEGIN_GCWEB_INTERFACE_BLOCK( pchInterfaceName ) \
|
||||
KeyValues *CreateWebAPIInterfaceMap_##pchInterfaceName() \
|
||||
{ \
|
||||
KeyValues *pkvInterface = new KeyValues( #pchInterfaceName );
|
||||
|
||||
#define DECLARE_GCWEBAPI_METHOD( pchMethodName, unVersion, eHTTPMethod, pchJobName, ePriv ) \
|
||||
{ \
|
||||
KeyValues *pkvMethod = pkvInterface->FindKey( pchMethodName #unVersion, true ); \
|
||||
pkvMethod->SetString( "name", pchMethodName ); \
|
||||
pkvMethod->SetInt( "version", unVersion ); \
|
||||
pkvMethod->SetInt( "http_method", eHTTPMethod ); \
|
||||
pkvMethod->SetString( "job_name", pchJobName); \
|
||||
pkvMethod->SetInt( "priv", ePriv );
|
||||
|
||||
#define REQUIRED_GCWEBAPI_PARAM( pchName, eType, pchDescription ) \
|
||||
{ \
|
||||
KeyValues *pkvParams = pkvMethod->FindKey( "params", true ); \
|
||||
AssertMsg( Q_stricmp( pchName, "format" ) != 0, "'format' is a magic reserved API param for specifying output format!" ); \
|
||||
KeyValues *pkvParam = pkvParams->FindKey( pchName, true ); \
|
||||
pkvParam->SetString( "description", pchDescription ); \
|
||||
pkvParam->SetInt( "type", eType ); \
|
||||
pkvParam->SetInt( "optional", 0 ); \
|
||||
}
|
||||
|
||||
#define OPTIONAL_GCWEBAPI_PARAM( pchName, eType, pchDescription ) \
|
||||
{ \
|
||||
KeyValues *pkvParams = pkvMethod->FindKey( "params", true ); \
|
||||
AssertMsg( Q_stricmp( pchName, "format" ) != 0, "'format' is a magic reserved API param for specifying output format!" ); \
|
||||
KeyValues *pkvParam = pkvParams->FindKey( pchName, true ); \
|
||||
pkvParam->SetString( "description", pchDescription ); \
|
||||
pkvParam->SetInt( "type", eType ); \
|
||||
pkvParam->SetInt( "optional", 1 ); \
|
||||
}
|
||||
|
||||
#define END_GCWEBAPI_METHOD() \
|
||||
}
|
||||
|
||||
|
||||
#define END_GCWEB_INTERFACE_BLOCK( pchInterfaceName ) \
|
||||
return pkvInterface; \
|
||||
} \
|
||||
CGCWebAPIInterfaceMapRegistrar g_Register_GCWebAPIInterfaceMapCreator_##pchInterfaceName( &CreateWebAPIInterfaceMap_##pchInterfaceName );
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // GCWEBAPI_H
|
||||
45
public/gcsdk/gcwebapikey.h
Normal file
45
public/gcsdk/gcwebapikey.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//========= Copyright ©, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: header for Web API key
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCWEBAPIKEY_H
|
||||
#define GCWEBAPIKEY_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
using GCSDK::CGCMsgBase;
|
||||
using GCSDK::WebAPIKey_t;
|
||||
using GCSDK::EWebAPIKeyStatus;
|
||||
|
||||
class CMsgWebAPIKey;
|
||||
|
||||
class CWebAPIKey
|
||||
{
|
||||
public:
|
||||
CWebAPIKey() { Clear(); }
|
||||
|
||||
void Clear();
|
||||
bool BIsValid() const { return (m_unAccountID != 0 || m_unPublisherGroupID != 0) && m_eStatus == GCSDK::k_EWebAPIKeyValid; }
|
||||
bool BIsAccountKey() const { return m_unAccountID != 0; }
|
||||
bool BIsPublisherKey() const { return m_unPublisherGroupID != 0; }
|
||||
uint32 GetAccountID() const { return m_unAccountID; }
|
||||
uint32 GetPublisherGroupID() const { return m_unPublisherGroupID; }
|
||||
uint32 GetID() const { return m_unWebAPIKeyID; }
|
||||
const char *GetDomain() const { return m_sDomain; }
|
||||
EWebAPIKeyStatus GetStatus() const { return m_eStatus; }
|
||||
|
||||
void SerializeIntoProtoBuf( CMsgWebAPIKey & apiKey ) const;
|
||||
void DeserializeFromProtoBuf( const CMsgWebAPIKey & apiKey );
|
||||
|
||||
private:
|
||||
EWebAPIKeyStatus m_eStatus;
|
||||
uint32 m_unAccountID; // set if key is for an account, 0 otherwise
|
||||
uint32 m_unPublisherGroupID; // set if key is for a publisher, 0 otherwise
|
||||
uint32 m_unWebAPIKeyID;
|
||||
CUtlString m_sDomain;
|
||||
};
|
||||
|
||||
#endif // GCWEBAPIKEY_H
|
||||
145
public/gcsdk/gcwgjobmgr.h
Normal file
145
public/gcsdk/gcwgjobmgr.h
Normal file
@@ -0,0 +1,145 @@
|
||||
#ifndef GCWGJOB_H
|
||||
#define GCWGJOB_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CGCWGJobMgr;
|
||||
|
||||
// defines a single parameter to a web api func
|
||||
struct WebApiParam_t
|
||||
{
|
||||
const char *m_pchParam;
|
||||
const char *m_pchDescription;
|
||||
bool m_bOptional; // true if optional
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Privilege type for WG requests
|
||||
// NOTE: This enum is a copy of EWegApiPrivilege from servercommon.h.
|
||||
enum EGCWebApiPrivilege
|
||||
{
|
||||
k_EGCWebApiPriv_None = 0, // doens't require any privileges
|
||||
k_EGCWebApiPriv_Account = 1, // user must have a Steam account with password set
|
||||
k_EGCWebApiPriv_Approved = 2, // user must not be blocked from community activity
|
||||
k_EGCWebApiPriv_Session = 3, // user must have a current Steam3 session
|
||||
k_EGCWebApiPriv_Support = 4, // user must have Support flag set
|
||||
k_EGCWebApiPriv_Admin = 5, // user must have Admin flag set
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Steamworks Application Editing -
|
||||
//
|
||||
// This represents a minimal requirement - The user must have some of the
|
||||
// EAppRights available to his account for a particular application.
|
||||
// This value is stored in the g_WebApiFuncs table.
|
||||
//
|
||||
// The functions dispatched to from the g_WebApiFuncs table are responsible
|
||||
// for doing finer grain permissions checks.
|
||||
// At this time, only the k_EAppRightManageCEG check is performed at the coarser grain.
|
||||
//
|
||||
// Some privileges such k_EAppRightEditInfo are implemented entirely within the
|
||||
// Web Server's .php code, as these rights do not manipulate data through the Web Gateway,
|
||||
// but manipulate through direct access to file system files and perforce operations !
|
||||
//
|
||||
k_EGCWebApiPriv_EditApp = 6, // user has some rights onto specific app - is publisher-affiliated and rights match app (or is admin)
|
||||
//
|
||||
// End Steamworks Application Editing -
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Steamworks Publisher Editing -
|
||||
//
|
||||
// These represent requests for particular rights involving the manipulation of
|
||||
// publisher data. k_EGCWebApiPriv_MemberPublisher only requires that the user be a member of a publisher,
|
||||
// whereas k_EGCWebApiPriv_EditPublisher specifically requires the user has k_EPubRightManagerUsers permission
|
||||
// within a particular publisher. That is because at this time k_EPubRightManagerUsers is the ONLY
|
||||
// right defined so the coarse grain view of k_EGCWebApiPriv_EditPublisher exactly matches the finer grain
|
||||
// view defined by EPubRights.
|
||||
//
|
||||
k_EGCWebApiPriv_MemberPublisher = 7, // user is publisher-affiliated with specific publisher (or is admin)
|
||||
k_EGCWebApiPriv_EditPublisher = 8, // user can edit specific publisher (or is admin)
|
||||
//
|
||||
// End Steamworks Publisher Editing -
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
k_EGCWebApiPriv_AccountOptional = 9, // validate the token if we get one but also allow public requests through
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct WebApiFunc_t
|
||||
{
|
||||
const char *m_pchRequestName;
|
||||
const char *m_pchRequestHandlerJobName;
|
||||
EGCWebApiPrivilege m_eRequiredPrivilege;
|
||||
WebApiParam_t m_rgParams[20];
|
||||
};
|
||||
|
||||
class CGCWGJobMgr
|
||||
{
|
||||
public:
|
||||
CGCWGJobMgr( );
|
||||
~CGCWGJobMgr();
|
||||
bool BHandleMsg( IMsgNetPacket *pNetPacket );
|
||||
static CUtlDict< const WebApiFunc_t* > &GetWGRequestMap();
|
||||
|
||||
static void SendErrorMessage( const CGCMsg<MsgGCWGRequest_t> & msg, const char *pchErrorMsg, int32 nResult );
|
||||
static void SetErrorMessage( KeyValues *pkvErr, const char *pchErrorMsg, int32 nResult );
|
||||
static void SendResponse( const CGCMsg<MsgGCWGRequest_t> & msg, KeyValues *pkvResponse, bool bResult );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
static void ValidateStatics( CValidator &validator );
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
protected:
|
||||
static void RegisterWGJob( const WebApiFunc_t *pWGJobType, const JobType_t *pJobCreationFunc );
|
||||
friend void GCWGJob_RegisterWGJobType( const WebApiFunc_t *pWGJobType, const JobType_t *pJobCreationFunc );
|
||||
|
||||
bool BVerifyPrivileges( const CGCMsg<MsgGCWGRequest_t> & msg, const WebApiFunc_t * pFunc );
|
||||
bool BVerifyParams( const CGCMsg<MsgGCWGRequest_t> & msg, const WebApiFunc_t * pFunc );
|
||||
};
|
||||
|
||||
inline void GCWGJob_RegisterWGJobType( const WebApiFunc_t *pWGJobType, const JobType_t *pJobCreationFunc )
|
||||
{
|
||||
CGCWGJobMgr::RegisterWGJob( pWGJobType, pJobCreationFunc );
|
||||
}
|
||||
|
||||
// declares a job as a wg job, require/optional params should be placed between begin and end declare.
|
||||
#define DECLARE_GCWG_JOB( gcbaseSubclass, jobclass, requestname, requiredprivilege ) \
|
||||
CJob *CreateWGJob_##jobclass( gcbaseSubclass *pvParent, void * pvStartParam ); \
|
||||
static const JobType_t g_JobType_##jobclass = { #jobclass, k_EGCMsgInvalid, k_EServerTypeGC, (JobCreationFunc_t)CreateWGJob_##jobclass }; \
|
||||
CJob *CreateWGJob_##jobclass( gcbaseSubclass *pvParent, void * pvStartParam ) \
|
||||
{ \
|
||||
CJob *job = CJob::AllocateJob<jobclass>( pvParent ); \
|
||||
Job_SetJobType( *job, &g_JobType_##jobclass ); \
|
||||
if ( pvStartParam ) job->SetStartParam( pvStartParam ); \
|
||||
return job; \
|
||||
} \
|
||||
static const WebApiFunc_t g_WGRequestInfo_##jobclass = { requestname, #jobclass, requiredprivilege, {
|
||||
|
||||
#define REQUIRED_GCWG_PARAM( pstrParameter, pstrDescription ) { pstrParameter, pstrDescription, false },
|
||||
|
||||
#define OPTIONAL_GCWG_PARAM( pstrParameter, pstrDescription ) { pstrParameter, pstrDescription, true },
|
||||
|
||||
#define END_DECLARE_GCWG_JOB( jobclass ) } }; \
|
||||
static class CRegWGJob_##jobclass \
|
||||
{ \
|
||||
public: CRegWGJob_##jobclass() \
|
||||
{ \
|
||||
GCWGJob_RegisterWGJobType( &g_WGRequestInfo_##jobclass, &g_JobType_##jobclass ); \
|
||||
} \
|
||||
} g_RegWGJob_##jobclass;
|
||||
|
||||
// quick and dirty - register a job with no required/optional parameters
|
||||
#define REG_GCWG_JOB( jobclass, requestname, requiredprivilege ) \
|
||||
DECLARE_WG_JOB( jobclass, requestname, requiredprivilege ) \
|
||||
END_DECLARE_GCWG_JOB( jobclass )
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif
|
||||
408
public/gcsdk/http.h
Normal file
408
public/gcsdk/http.h
Normal file
@@ -0,0 +1,408 @@
|
||||
//====== Copyright © 1996-2010, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: HTTP related enums and objects, stuff that both clients and server use should go here
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef HTTP_H
|
||||
#define HTTP_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "steam/steamhttpenums.h"
|
||||
#include "tier1/keyvalues.h"
|
||||
#include "tier1/netadr.h"
|
||||
|
||||
class CMsgHttpRequest;
|
||||
class CMsgHttpResponse;
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
// Container class for useful parsing methods and other utility code used by client or server http code
|
||||
class CHTTPUtil
|
||||
{
|
||||
public:
|
||||
// Check if a status code allows a body to exist
|
||||
static bool BStatusCodeAllowsBody( EHTTPStatusCode eHTTPStatus );
|
||||
|
||||
};
|
||||
|
||||
|
||||
class CHTTPRequest;
|
||||
class CHTTPResponse;
|
||||
class CHTTPServerClientConnection;
|
||||
|
||||
// A request parameter, either get or post, or parsed out of the URL
|
||||
class CHTTPRequestParam
|
||||
{
|
||||
public:
|
||||
|
||||
// Default constructor
|
||||
CHTTPRequestParam()
|
||||
{
|
||||
m_pData = NULL;
|
||||
m_cubDataLength = 0;
|
||||
}
|
||||
|
||||
// Constructor with data
|
||||
CHTTPRequestParam( const char *pchName, uint8 *pData, uint32 cubDataLen )
|
||||
{
|
||||
m_strName = pchName;
|
||||
|
||||
// We always allocate an extra byte to null terminate, so we can treat as a string safely
|
||||
// even though treating as a string may truncate early if the data is binary.
|
||||
m_pData = new uint8[cubDataLen+1];
|
||||
Q_memcpy( m_pData, pData, cubDataLen );
|
||||
m_pData[cubDataLen] = 0;
|
||||
|
||||
m_cubDataLength = cubDataLen;
|
||||
}
|
||||
|
||||
// Copy constructor (does a deep copy)
|
||||
CHTTPRequestParam(const CHTTPRequestParam& rhs)
|
||||
{
|
||||
m_strName = rhs.m_strName;
|
||||
|
||||
m_cubDataLength = rhs.m_cubDataLength;
|
||||
|
||||
m_pData = new uint8[rhs.m_cubDataLength+1];
|
||||
Q_memcpy( m_pData, rhs.m_pData, rhs.m_cubDataLength );
|
||||
m_pData[m_cubDataLength] = 0;
|
||||
|
||||
}
|
||||
|
||||
// Operator =
|
||||
const CHTTPRequestParam& operator=(const CHTTPRequestParam& rhs)
|
||||
{
|
||||
if ( m_pData )
|
||||
delete[] m_pData;
|
||||
|
||||
m_strName = rhs.m_strName;
|
||||
|
||||
m_cubDataLength = rhs.m_cubDataLength;
|
||||
|
||||
m_pData = new uint8[rhs.m_cubDataLength+1];
|
||||
Q_memcpy( m_pData, rhs.m_pData, rhs.m_cubDataLength );
|
||||
m_pData[m_cubDataLength] = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~CHTTPRequestParam()
|
||||
{
|
||||
if ( m_pData )
|
||||
delete[] m_pData;
|
||||
|
||||
m_pData = NULL;
|
||||
}
|
||||
|
||||
// Set the request parameters name
|
||||
void SetName( const char *pchName )
|
||||
{
|
||||
m_strName = pchName;
|
||||
}
|
||||
|
||||
// Set the data for the parameter, this is expected to either be binary
|
||||
// data, or to be in string form, the conversion Getters will all Q_atoi
|
||||
// or such when converting to non-string forms.
|
||||
void SetRawData( uint8 *pData, uint32 cubDataLen )
|
||||
{
|
||||
if ( m_pData )
|
||||
delete[] m_pData;
|
||||
|
||||
m_cubDataLength = cubDataLen;
|
||||
|
||||
m_pData = new uint8[cubDataLen+1];
|
||||
Q_memcpy( m_pData, pData, cubDataLen );
|
||||
m_pData[m_cubDataLength] = 0;
|
||||
}
|
||||
|
||||
|
||||
// Get the name
|
||||
const char *GetName() const
|
||||
{
|
||||
return m_strName.Get();
|
||||
}
|
||||
|
||||
|
||||
// Get pointer to the data
|
||||
const uint8 *GetPubData() const
|
||||
{
|
||||
return m_pData;
|
||||
}
|
||||
|
||||
|
||||
// Get the length of the data
|
||||
uint32 GetCubData() const
|
||||
{
|
||||
return m_cubDataLength;
|
||||
}
|
||||
|
||||
|
||||
// Get the data as a string
|
||||
const char *ToString() const
|
||||
{
|
||||
return (char *)m_pData;
|
||||
}
|
||||
|
||||
|
||||
// Get the data converted to an int32
|
||||
int32 ToInt32() const
|
||||
{
|
||||
return Q_atoi( (char *)m_pData );
|
||||
}
|
||||
|
||||
|
||||
// Get the data converted to an int64
|
||||
int64 ToInt64() const
|
||||
{
|
||||
return Q_atoi64( (char*)m_pData );
|
||||
}
|
||||
|
||||
|
||||
// Get the data converted to an uint32
|
||||
uint32 ToUInt32() const
|
||||
{
|
||||
return (uint32)V_atoui64( (char *)m_pData );
|
||||
}
|
||||
|
||||
|
||||
// Get the data converted to an uint64
|
||||
uint64 ToUInt64() const
|
||||
{
|
||||
return V_atoui64( (char *)m_pData );
|
||||
}
|
||||
|
||||
// Get the data converted to a float
|
||||
float ToFloat() const
|
||||
{
|
||||
return Q_atof( (char *)m_pData );
|
||||
}
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName )
|
||||
{
|
||||
VALIDATE_SCOPE();
|
||||
ValidateObj( m_strName );
|
||||
validator.ClaimMemory( m_pData );
|
||||
}
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
private:
|
||||
CUtlString m_strName;
|
||||
uint32 m_cubDataLength;
|
||||
uint8 *m_pData;
|
||||
};
|
||||
|
||||
class CHTTPRequest
|
||||
{
|
||||
public:
|
||||
CHTTPRequest();
|
||||
CHTTPRequest( EHTTPMethod eMethod, const char *pchHost, const char *pchRelativeURL );
|
||||
CHTTPRequest( EHTTPMethod eMethod, const char *pchAbsoluteURL );
|
||||
|
||||
virtual ~CHTTPRequest();
|
||||
|
||||
// Get the method type for the request (ie, GET, POST, etc)
|
||||
EHTTPMethod GetEHTTPMethod() { return m_eRequestMethod; }
|
||||
|
||||
// Get the relative URL for the request
|
||||
const char *GetURL() { return m_strURL.Get(); }
|
||||
|
||||
// Get the value of a GET parameter, using the default value if not set. This is case-insensitive by default.
|
||||
const CHTTPRequestParam *GetGETParam( const char *pchGetParamName, bool bMatchCase = false ) const;
|
||||
const char *GetGETParamString( const char *pchGetParamName, const char *pchDefault, bool bMatchCase = false ) const;
|
||||
bool GetGETParamBool( const char *pchGetParamName, bool bDefault, bool bMatchCase = false ) const;
|
||||
int32 GetGETParamInt32( const char *pchGetParamName, int32 nDefault, bool bMatchCase = false ) const;
|
||||
uint32 GetGETParamUInt32( const char *pchGetParamName, uint32 unDefault, bool bMatchCase = false ) const;
|
||||
int64 GetGETParamInt64( const char *pchGetParamName, int64 nDefault, bool bMatchCase = false ) const;
|
||||
uint64 GetGETParamUInt64( const char *pchGetParamName, uint64 unDefault, bool bMatchCase = false ) const;
|
||||
float GetGETParamFloat( const char *pchGetParamName, float fDefault, bool bMatchCase = false ) const;
|
||||
|
||||
|
||||
// Get the value of a POST parameter, using the default value if not set. This is case-insensitive by default.
|
||||
const CHTTPRequestParam *GetPOSTParam( const char *pchPostParamName, bool bMatchCase = false ) const;
|
||||
const char *GetPOSTParamString( const char *pchGetParamName, const char *pchDefault, bool bMatchCase = false ) const;
|
||||
bool GetPOSTParamBool( const char *pchGetParamName, bool bDefault, bool bMatchCase = false ) const;
|
||||
int32 GetPOSTParamInt32( const char *pchGetParamName, int32 nDefault, bool bMatchCase = false ) const;
|
||||
uint32 GetPOSTParamUInt32( const char *pchGetParamName, uint32 unDefault, bool bMatchCase = false ) const;
|
||||
int64 GetPOSTParamInt64( const char *pchGetParamName, int64 nDefault, bool bMatchCase = false ) const;
|
||||
uint64 GetPOSTParamUInt64( const char *pchGetParamName, uint64 unDefault, bool bMatchCase = false ) const;
|
||||
float GetPOSTParamFloat( const char *pchGetParamName, float fDefault, bool bMatchCase = false ) const;
|
||||
|
||||
// Add a GET param to the request
|
||||
void SetGETParamString( const char *pchGetParamName, const char *pString ) { SetGETParamRaw( pchGetParamName, (uint8*)pString, Q_strlen(pString) ); }
|
||||
void SetGETParamBool( const char *pchPostParamName, bool bValue ) { SetGETParamRaw( pchPostParamName, (uint8*)(bValue ? "1" : "0"), 1 ); }
|
||||
void SetGETParamInt32( const char *pchPostParamName, int32 nValue ) { CFmtStrN<32> str( "%d", nValue ); SetGETParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetGETParamUInt32( const char *pchPostParamName, uint32 unValue ) { CFmtStrN<32> str( "%u", unValue ); SetGETParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetGETParamInt64( const char *pchPostParamName, int64 nValue ) { CFmtStrN<32> str( "%lld", nValue ); SetGETParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetGETParamUInt64( const char *pchPostParamName, uint64 unValue ) { CFmtStrN<32> str( "%llu", unValue ); SetGETParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetGETParamFloat( const char *pchPostParamName, float fValue ) { CFmtStrN<32> str( "%f", fValue ); SetGETParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
|
||||
// Adds a GET param containing raw data to the request. If you are using the Web API, you probably do not want this function
|
||||
void SetGETParamRaw( const char *pchGetParamName, uint8 *pData, uint32 cubDataLen );
|
||||
|
||||
// Add a POST param to the request given a string for the name and value
|
||||
void SetPOSTParamString( const char *pchPostParamName, const char *pString ) { SetPOSTParamRaw( pchPostParamName, (uint8*)pString, Q_strlen(pString) ); }
|
||||
void SetPOSTParamBool( const char *pchPostParamName, bool bValue ) { SetPOSTParamRaw( pchPostParamName, (uint8*)(bValue ? "1" : "0"), 1 ); }
|
||||
void SetPOSTParamInt32( const char *pchPostParamName, int32 nValue ) { CFmtStrN<32> str( "%d", nValue ); SetPOSTParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetPOSTParamUInt32( const char *pchPostParamName, uint32 unValue ) { CFmtStrN<32> str( "%u", unValue ); SetPOSTParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetPOSTParamInt64( const char *pchPostParamName, int64 nValue ) { CFmtStrN<32> str( "%lld", nValue ); SetPOSTParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetPOSTParamUInt64( const char *pchPostParamName, uint64 unValue ) { CFmtStrN<32> str( "%llu", unValue ); SetPOSTParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
void SetPOSTParamFloat( const char *pchPostParamName, float fValue ) { CFmtStrN<32> str( "%f", fValue ); SetPOSTParamRaw( pchPostParamName, (uint8*)str.Access(), str.Length() ); }
|
||||
|
||||
// Adds a POST param containing raw data to the request. If you are using the Web API, you probably do not want this function
|
||||
void SetPOSTParamRaw( const char *pchPostParamName, uint8 *pData, uint32 cubDataLen );
|
||||
|
||||
// Get count of POST params in the request
|
||||
uint32 GetPOSTParamCount() { return m_vecPostParams.Count(); }
|
||||
|
||||
// Get count of GET params in the request
|
||||
uint32 GetGETParamCount() { return m_vecGetParams.Count(); }
|
||||
|
||||
// Fetch a request header by header name and convert it to a time value
|
||||
RTime32 GetRequestHeaderTimeValue( const char *pchRequestHeaderName, RTime32 rtDefault = 0 );
|
||||
|
||||
// Fetch a request headers value by header name
|
||||
const char *GetRequestHeaderValue( const char *pchRequestHeaderName, const char *pchDefault = NULL ) { return m_pkvRequestHeaders->GetString( pchRequestHeaderName, pchDefault ); }
|
||||
|
||||
// Check if the request has any value for the header with the given name
|
||||
bool BHasRequestHeader( const char *pchRequestHeaderName ) { return (m_pkvRequestHeaders->GetString(pchRequestHeaderName, NULL ) ? true : false); }
|
||||
|
||||
// Set the method for the request object
|
||||
void SetEHTTPMethod( EHTTPMethod eMethod ) { m_eRequestMethod = eMethod; }
|
||||
|
||||
// Set the relative URL for the request
|
||||
void SetURL( const char *pchURL )
|
||||
{
|
||||
AssertMsg1( pchURL && pchURL[0] == '/', "URLs must start with the slash (/) character. Param: %s", pchURL );
|
||||
m_strURL = pchURL;
|
||||
}
|
||||
|
||||
// Set a header field for the request
|
||||
void SetRequestHeaderValue( const char *pchHeaderName, const char *pchHeaderString ) { m_pkvRequestHeaders->SetString( pchHeaderName, pchHeaderString ); }
|
||||
|
||||
// Set body data
|
||||
bool BSetBodyData( uint8 *pubData, int cubData );
|
||||
|
||||
// Get direct access to body data buffer
|
||||
CUtlBuffer *GetBodyBuffer() { return &m_bufBody; }
|
||||
|
||||
// Set hostname for request to target (or host it was received on)
|
||||
void SetHostname( const char *pchHost ) { m_strHostname.Set( pchHost ); }
|
||||
void SetHostnameDirect( const char *pchHost, uint32 unLength ) { m_strHostname.SetDirect( pchHost, unLength ); }
|
||||
const char *GetHostname() { return m_strHostname.Get(); }
|
||||
|
||||
// Get direct access to GET param vector
|
||||
CUtlVector<CHTTPRequestParam> &GetGETParamVector() { return m_vecGetParams; }
|
||||
CUtlVector<CHTTPRequestParam> &GetPOSTParamVector() { return m_vecPostParams; }
|
||||
|
||||
// Get direct access to request headers
|
||||
KeyValues *GetRequestHeadersKV() { return m_pkvRequestHeaders; }
|
||||
|
||||
// writes the request into a protobuf for use in messages
|
||||
void SerializeIntoProtoBuf( CMsgHttpRequest & request ) const;
|
||||
|
||||
// reads the request out of a protobuf from a message
|
||||
void DeserializeFromProtoBuf( const CMsgHttpRequest & apiKey );
|
||||
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
protected:
|
||||
// Common initialization code
|
||||
void Init();
|
||||
|
||||
// Method for this request
|
||||
EHTTPMethod m_eRequestMethod;
|
||||
|
||||
// Header values and get param values. Headers are defined with the name case-insensitive, so KV is a good fit.
|
||||
KeyValues *m_pkvRequestHeaders;
|
||||
|
||||
// Get/Post params, these can be case sensitive, so keyvalues would be a bad fit. They would also be a bad
|
||||
// fit for KV since we may use lots of random param names and use up space in the global string table.
|
||||
CUtlVector<CHTTPRequestParam> m_vecGetParams;
|
||||
CUtlVector<CHTTPRequestParam> m_vecPostParams;
|
||||
|
||||
// Host to issue the request to (or host received on server side)
|
||||
// Note: This is not the Host: header value, but rather the actual address the request is received on
|
||||
// is meant to be issued to. The Host: header may be different containing a virtual hostname.
|
||||
CUtlString m_strHostname;
|
||||
|
||||
// Relative URL
|
||||
CUtlString m_strURL;
|
||||
|
||||
// Body of the request
|
||||
CUtlBuffer m_bufBody;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A wrapper to CHTTPRequest for Steam WebAPIs. Host, mode, and API key
|
||||
// are set automatically
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSteamAPIRequest : public CHTTPRequest
|
||||
{
|
||||
public:
|
||||
CSteamAPIRequest( EHTTPMethod eMethod, const char *pchInterface, const char *pchMethod, int nVersion );
|
||||
};
|
||||
|
||||
|
||||
class CHTTPResponse
|
||||
{
|
||||
public:
|
||||
CHTTPResponse();
|
||||
virtual ~CHTTPResponse();
|
||||
|
||||
// Get a specific headers data
|
||||
const char *GetResponseHeaderValue( const char *pchName, const char *pchDefault = NULL ) { return m_pkvResponseHeaders->GetString( pchName, pchDefault ); }
|
||||
|
||||
// Set a specific headers data, will clobber any existing value for that header
|
||||
virtual void SetResponseHeaderValue( const char *pchName, const char *pchValue ) { m_pkvResponseHeaders->SetString( pchName, pchValue ); }
|
||||
|
||||
// Set status code for response
|
||||
virtual void SetStatusCode( EHTTPStatusCode eStatusCode ) { m_eStatusCode = eStatusCode; }
|
||||
|
||||
// Set the expiration header based on a time delta from now
|
||||
void SetExpirationHeaderDeltaFromNow( int32 nSecondsFromNow );
|
||||
|
||||
// Set the expiration header based on a time delta from now
|
||||
void SetHeaderTimeValue( const char *pchHeaderName, RTime32 rtTimestamp );
|
||||
|
||||
// Get the entire headers KV (in case you want to iterate them all or such)
|
||||
KeyValues *GetResponseHeadersKV() { return m_pkvResponseHeaders; }
|
||||
|
||||
// Accessors to the body data in the response
|
||||
uint8 *GetPubBody() { return (uint8*)m_bufBody.Base(); }
|
||||
uint32 GetCubBody() { return m_bufBody.TellPut(); }
|
||||
CUtlBuffer *GetBodyBuffer() { return &m_bufBody; }
|
||||
|
||||
// Accessor
|
||||
EHTTPStatusCode GetStatusCode() { return m_eStatusCode; }
|
||||
|
||||
// writes the response into a protobuf for use in messages
|
||||
void SerializeIntoProtoBuf( CMsgHttpResponse & response ) const;
|
||||
|
||||
// reads the response out of a protobuf from a message
|
||||
void DeserializeFromProtoBuf( const CMsgHttpResponse & response );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
protected:
|
||||
EHTTPStatusCode m_eStatusCode;
|
||||
CUtlBuffer m_bufBody;
|
||||
KeyValues *m_pkvResponseHeaders;
|
||||
};
|
||||
|
||||
}
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // HTTP_H
|
||||
460
public/gcsdk/job.h
Normal file
460
public/gcsdk/job.h
Normal file
@@ -0,0 +1,460 @@
|
||||
//========= Copyright 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GC_JOB_H
|
||||
#define GC_JOB_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
#include "tier1/functors.h"
|
||||
#include "workthreadpool.h"
|
||||
|
||||
class GCConVar;
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
class CJobMgr;
|
||||
class CLock;
|
||||
class CJob;
|
||||
class IMsgNetPacket;
|
||||
|
||||
// job creation function responsible for allocating the job of the specific type
|
||||
typedef CJob *(*JobCreationFunc_t)( void *pvServerParent, void * pvStartParam );
|
||||
// job routing function, which allows for controlling message routing through the system. The GCs that should have the message sent to it should be added to the vector. It is fine to add the actual self GC
|
||||
typedef void (*JobRoutingFunc_t)( CUtlVector< uint32 >& vecGCsToSendTo, IMsgNetPacket *pNetPacket );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: static job information
|
||||
// contains information relevant to one type of CJob
|
||||
//-----------------------------------------------------------------------------
|
||||
struct JobType_t
|
||||
{
|
||||
const char *m_pchName; // name of this type of job
|
||||
MsgType_t m_eCreationMsg; // network message that creates this job
|
||||
uint32 m_nValidContexts; // a bit field indicating which contexts this message can be called from (i.e. from a server, or from a client, etc)
|
||||
JobCreationFunc_t m_pJobFactory; // virtual constructor
|
||||
JobRoutingFunc_t m_pJobRouter; // routing function for this job
|
||||
const GCConVar* m_pControlCV; // optional console variable that can be used to disable this message
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: reason as to why the current job has yielded to the main thread (paused)
|
||||
// if this is updated, k_prgchJobPauseReason[] in job.cpp also needs to be updated
|
||||
//-----------------------------------------------------------------------------
|
||||
enum EJobPauseReason
|
||||
{
|
||||
k_EJobPauseReasonNone,
|
||||
k_EJobPauseReasonNotStarted,
|
||||
k_EJobPauseReasonNetworkMsg,
|
||||
k_EJobPauseReasonSleepForTime,
|
||||
k_EJobPauseReasonWaitingForLock,
|
||||
k_EJobPauseReasonYield,
|
||||
k_EJobPauseReasonSQL,
|
||||
k_EJobPauseReasonWorkItem,
|
||||
|
||||
k_EJobPauseReasonCount
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: contains information used to route a message to a job, or to
|
||||
// create a new job from that message type
|
||||
//-----------------------------------------------------------------------------
|
||||
struct JobMsgInfo_t
|
||||
{
|
||||
MsgType_t m_eMsg;
|
||||
JobID_t m_JobIDSource;
|
||||
JobID_t m_JobIDTarget;
|
||||
|
||||
JobMsgInfo_t()
|
||||
{
|
||||
m_eMsg = (MsgType_t)0;
|
||||
m_JobIDSource = k_GIDNil;
|
||||
m_JobIDTarget = k_GIDNil;
|
||||
}
|
||||
|
||||
JobMsgInfo_t( MsgType_t eMsg, JobID_t jobIDSource, JobID_t jobIDTarget )
|
||||
{
|
||||
m_eMsg = eMsg;
|
||||
m_JobIDSource = jobIDSource;
|
||||
m_JobIDTarget = jobIDTarget;
|
||||
}
|
||||
};
|
||||
|
||||
typedef void (CJob::*JobThreadFunc_t)();
|
||||
|
||||
#define BYieldingAcquireLock( lock ) _BYieldingAcquireLock( lock, __FILE__, __LINE__ )
|
||||
#define BAcquireLockImmediate( lock ) _BAcquireLockImmediate( lock, __FILE__, __LINE__ )
|
||||
#define ReleaseLock( lock ) _ReleaseLock( lock, false, __FILE__, __LINE__ )
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A job is any server operation that requires state. Typically, we use jobs for
|
||||
// operations that need to pause waiting for responses from other servers. The
|
||||
// job object persists the state of the operation while it waits, and the reply
|
||||
// from the remote server re-activates the job.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CJob
|
||||
{
|
||||
public:
|
||||
// Constructors & destructors, when overriding job name a static string pointer must be used
|
||||
explicit CJob( CJobMgr &jobMgr, char const *pchJobName = NULL );
|
||||
virtual ~CJob();
|
||||
|
||||
// starts the job, storing off the network msg and calling it's Run() function
|
||||
void StartJobFromNetworkMsg( IMsgNetPacket *pNetPacket, const JobID_t &gidJobIDSrc, uint32 nContextMask );
|
||||
|
||||
// accessors
|
||||
JobID_t GetJobID() const { return m_JobID; }
|
||||
|
||||
// called to start a job. The default behavior of starting a job is to start it scheduled (i.e. delayed). This is largely deprecated, and the more explicit versions
|
||||
// below should be used instead
|
||||
void StartJob( void * pvStartParam );
|
||||
// schedules the job for execution, but does not interrupt the currently running job. Effectively starts the job on the yielding list as if it had immediately yielded
|
||||
// although is more efficient than actually doing so
|
||||
void StartJobDelayed( void * pvStartParam );
|
||||
// starts a job immediately, interrupting the current job if one is already running. This should only be used in special cases, and in a lot of ways should be considered
|
||||
// yielding in that another job can run and perform modifications, although the caller will receive attention back the first time that the inner job itself yields
|
||||
void StartJobImmediate( void * pvStartParam );
|
||||
|
||||
// string name of the job
|
||||
const char *GetName() const;
|
||||
// return reason why we're paused
|
||||
EJobPauseReason GetPauseReason() const { return m_ePauseReason; }
|
||||
// string description of why we're paused
|
||||
const char *GetPauseReasonDescription() const;
|
||||
// return time at which this job was last paused or continued
|
||||
const CJobTime& GetTimeSwitched() const { return m_STimeSwitched; }
|
||||
// return microseconds run since we were last continued
|
||||
uint64 GetMicrosecondsRun() const { return m_FastTimerDelta.GetDurationInProgress().GetUlMicroseconds(); }
|
||||
bool BJobNeedsToHeartbeat() const { return ( m_STimeNextHeartbeat.CServerMicroSecsPassed() >= 0 ); }
|
||||
|
||||
// --- locking pointers
|
||||
bool _BYieldingAcquireLock( CLock *pLock, const char *filename = "unknown", int line = 0 );
|
||||
bool _BAcquireLockImmediate( CLock *pLock, const char *filename = "unknown", int line = 0 );
|
||||
void _ReleaseLock( CLock *pLock, bool bForce = false, const char *filename = "unknown", int line = 0 );
|
||||
void ReleaseLocks();
|
||||
bool BJobHoldsLock( uint16 nType, uint64 nSubType ) const;
|
||||
bool BJobHoldsLock( const CLock* pLock ) const;
|
||||
|
||||
/// If we hold any locks, spew about them and release them.
|
||||
/// This is useful for long running jobs, to make sure they don't leak
|
||||
/// locks that never get cleaned up
|
||||
void ShouldNotHoldAnyLocks();
|
||||
|
||||
// --- general methods for waiting for events
|
||||
// Simple yield to other jobs until Run() called again
|
||||
bool BYield();
|
||||
// Yield IF JobMgr thinks we need to based on how long we've run and our priority
|
||||
bool BYieldIfNeeded( bool *pbYielded = NULL );
|
||||
// waits for a set amount of time
|
||||
bool BYieldingWaitTime( uint32 m_cMicrosecondsToSleep );
|
||||
bool BYieldingWaitOneFrame();
|
||||
// waits for another network msg, returning false if none returns
|
||||
bool BYieldingWaitForMsg( IMsgNetPacket **ppNetPacket );
|
||||
bool BYieldingWaitForMsg( CGCMsgBase *pMsg, MsgType_t eMsg );
|
||||
bool BYieldingWaitForMsg( CProtoBufMsgBase *pMsg, MsgType_t eMsg );
|
||||
|
||||
// waits for another job(s) to complete
|
||||
bool BYieldingWaitForJob( JobID_t jobToWaitFor );
|
||||
bool BYieldingWaitForJobs( const CUtlVector<JobID_t> &vecJobsToWaitFor );
|
||||
|
||||
#ifdef GC
|
||||
void SOVALIDATE_SetSteamID( const CSteamID steamID ) { m_SOCacheValidSteamID = steamID; }
|
||||
CSteamID SOVALIDATE_GetSteamID() const { return m_SOCacheValidSteamID; }
|
||||
void VALIDATE_SetJobAccessType( uint32 nAccess) { m_nGCJobAccessType = nAccess; }
|
||||
uint32 VALIDATE_GetJobAccessType() const { return m_nGCJobAccessType; }
|
||||
bool BYieldingWaitForMsg( CGCMsgBase *pMsg, MsgType_t eMsg, const CSteamID &expectedID );
|
||||
bool BYieldingWaitForMsg( CProtoBufMsgBase *pMsg, MsgType_t eMsg, const CSteamID &expectedID );
|
||||
bool BYieldingRunQuery( CGCSQLQueryGroup *pQueryGroup, ESchemaCatalog eSchemaCatalog );
|
||||
#endif
|
||||
|
||||
void RecordWaitTimeout() { m_flags.m_bits.m_bWaitTimeout = true; }
|
||||
|
||||
// wait for pending work items before deleting job
|
||||
void WaitForThreadFuncWorkItemBlocking();
|
||||
|
||||
// waits for a work item completion callback
|
||||
// You can pass a string that describes what sort of work item you are waiting on.
|
||||
// WARNING: This function saves the pointer to the string, it doesn't copy the string
|
||||
bool BYieldingWaitForWorkItem( const char *pszWorkItemName = NULL );
|
||||
|
||||
// adds this work item to threaded work pool and waits for it
|
||||
bool BYieldingWaitForThreadFuncWorkItem( CWorkItem * );
|
||||
|
||||
// calls a local function in a thread, and yields until it's done
|
||||
bool BYieldingWaitForThreadFunc( CFunctor *jobFunctor );
|
||||
|
||||
// creates a job
|
||||
template <typename JOB_TYPE, typename PARAM_TYPE>
|
||||
static JOB_TYPE *AllocateJob( PARAM_TYPE *pParam )
|
||||
{
|
||||
return new JOB_TYPE( pParam );
|
||||
}
|
||||
// delete a job (the job knows what allocator to use)
|
||||
static void DeleteJob( CJob *pJob );
|
||||
|
||||
void SetStartParam( void * pvStartParam ) { Assert( NULL == m_pvStartParam ); m_pvStartParam = pvStartParam; }
|
||||
void SetFromFromMsg( bool bRunFromMsg ) { m_bRunFromMsg = true; }
|
||||
|
||||
void AddPacketToList( IMsgNetPacket *pNetPacket, const JobID_t gidJobIDSrc );
|
||||
// marks a packet as being finished with, releases the packet and frees the memory
|
||||
void ReleaseNetPacket( IMsgNetPacket *pNetPacket );
|
||||
|
||||
void EndPause( EJobPauseReason eExpectedState );
|
||||
|
||||
// Generate an assertion in the coroutine of this job
|
||||
// (creating a minidump). Useful for inspecting stuck jobs
|
||||
void GenerateAssert( const char *pchMsg = NULL );
|
||||
|
||||
//called to determine if the requested context is valid. If multiple contexts are provided, this will return true only if ALL the contexts are valid
|
||||
bool BHasContext( uint32 nContext ) const { return ( m_nContextMask & nContext ) == nContext; }
|
||||
uint32 GetContexts() const { return m_nContextMask; }
|
||||
|
||||
//called to control the default behavior for starting jobs, immediate, or delayed
|
||||
static void SetStartDefaultJobsDelayed( bool bStartJobsDelayed ) { s_bStartDefaultJobsDelayed = bStartJobsDelayed; }
|
||||
|
||||
// accessor to get access to the JobMgr from the server we belong to
|
||||
CJobMgr &GetJobMgr();
|
||||
|
||||
protected:
|
||||
// main job implementation, in the coroutine. Every job must implement at least one of these methods.
|
||||
virtual bool BYieldingRunJob( void * pvStartParam ) { Assert( false ); return true; } // implement this if your job can be started directly
|
||||
virtual bool BYieldingRunJobFromMsg( IMsgNetPacket * pNetPacket ) { Assert( false ); return true; } // implement this if your job can be started by a network message
|
||||
|
||||
// Can be overridden to return a different timeout per job class
|
||||
virtual uint32 CHeartbeatsBeforeTimeout();
|
||||
|
||||
// Called by CJobMgr to send heartbeat message to our listeners during long operations
|
||||
void Heartbeat();
|
||||
|
||||
|
||||
uint32 m_bRunFromMsg:1,
|
||||
m_bWorkItemCanceled:1, // true if the work item we were waiting on was canceled
|
||||
m_bIsTest:1,
|
||||
m_bIsLongRunning:1;
|
||||
|
||||
private:
|
||||
// starts the coroutine that activates the job
|
||||
void InitCoroutine();
|
||||
|
||||
// continues the current job
|
||||
void Continue();
|
||||
|
||||
// break into this coroutine - can only be called from OUTSIDE this coroutine
|
||||
void Debug();
|
||||
|
||||
// pauses the current job - can only be called from inside a coroutine
|
||||
void Pause( EJobPauseReason eReason, const char *pszResourceName );
|
||||
|
||||
static void BRunProxy( void *pvThis );
|
||||
|
||||
JobID_t m_JobID; // Our unique identifier.
|
||||
HCoroutine m_hCoroutine;
|
||||
void * m_pvStartParam; // Start params for our job, if any
|
||||
// all these flags indicate some kind of failure and we will want to report them
|
||||
union {
|
||||
struct {
|
||||
uint32
|
||||
m_bJobFailed:1, // true if BYieldingRunJob returned false
|
||||
m_bLocksFailed:1,
|
||||
m_bLocksLongHeld:1,
|
||||
m_bLocksLongWait:1,
|
||||
m_bWaitTimeout:1,
|
||||
m_bLongInterYield:1,
|
||||
m_bTimeoutNetMsg:1,
|
||||
m_bTimeoutOther:1,
|
||||
m_uUnused:24;
|
||||
} m_bits;
|
||||
uint32 m_uFlags;
|
||||
} m_flags;
|
||||
int m_cLocksAttempted;
|
||||
int m_cLocksWaitedFor;
|
||||
EJobPauseReason m_ePauseReason;
|
||||
const char *m_pszPauseResourceName;
|
||||
MsgType_t m_unWaitMsgType;
|
||||
CJobTime m_STimeStarted; // time (frame) at which this job started
|
||||
CJobTime m_STimeSwitched; // time (frame) at which we were last paused or continued
|
||||
CJobTime m_STimeNextHeartbeat; // Time at which next heartbeat should be performed
|
||||
CFastTimer m_FastTimerDelta; // How much time we've been running for without yielding
|
||||
CCycleCount m_cyclecountTotal; // Total runtime
|
||||
uint32 m_nContextMask; // The context that this job was created in. Typically only used for message jobs to indicate the initiator of the message
|
||||
CJob *m_pJobPrev; // the job that launched us
|
||||
|
||||
// lock manipulation
|
||||
void _SetLock( CLock *pLock, const char *filename, int line );
|
||||
void UnsetLock( CLock *pLock );
|
||||
void PassLockToJob( CJob *pNewJob, CLock *pLock );
|
||||
void OnLockDeleted( CLock *pLock );
|
||||
void AddJobToNotifyOnLockRelease( CJob *pJob );
|
||||
CUtlVectorFixedGrowable< CLock *, 2 > m_vecLocks;
|
||||
CLock *m_pWaitingOnLock; // lock we're waiting on, if any
|
||||
const char *m_pWaitingOnLockFilename;
|
||||
int m_waitingOnLockLine;
|
||||
CJob *m_pJobToNotifyOnLockRelease; // other job that wants this lock
|
||||
CWorkItem *m_pWaitingOnWorkItem; // set if job is waiting for this work item
|
||||
|
||||
#ifdef GC
|
||||
//context flags indicating what this job can access. Temporary and only for validating access on the GC
|
||||
uint32 m_nGCJobAccessType;
|
||||
//the steam ID that we are stating is safe to access. This is temporary to validate jobs during the split of the GC
|
||||
CSteamID m_SOCacheValidSteamID;
|
||||
#endif
|
||||
|
||||
CJobMgr &m_JobMgr; // our job manager
|
||||
CUtlVectorFixedGrowable< IMsgNetPacket *, 1 > m_vecNetPackets; // list of tcp packets currently held by this job (ie, needing release on job exit)
|
||||
|
||||
// pointer to our own static job info
|
||||
struct JobType_t const *m_pJobType;
|
||||
|
||||
// Name of the job for when it's not registered
|
||||
const char *m_pchJobName;
|
||||
|
||||
// setting the job info
|
||||
friend void Job_SetJobType( CJob &job, const JobType_t *pJobType );
|
||||
friend class CJobMgr;
|
||||
friend class CLock;
|
||||
|
||||
// used to store the memory allocation stack
|
||||
CUtlMemory< unsigned char > m_memAllocStack;
|
||||
|
||||
static bool s_bStartDefaultJobsDelayed;
|
||||
};
|
||||
|
||||
|
||||
// Only one job can be running at a time. We keep a global accessor to it.
|
||||
extern CJob *g_pJobCur;
|
||||
inline CJob &GJobCur() { Assert( g_pJobCur != NULL ); return *g_pJobCur; }
|
||||
|
||||
#define AssertRunningJob() { Assert( NULL != g_pJobCur ); }
|
||||
#define AssertRunningThisJob() { Assert( this == g_pJobCur ); }
|
||||
#define AssertNotRunningThisJob() { Assert( this != g_pJobCur ); }
|
||||
#define AssertNotRunningJob() { Assert( NULL == g_pJobCur ); }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: simple locking class
|
||||
// add this object to any classes you want jobs to be able to lock
|
||||
//-----------------------------------------------------------------------------
|
||||
#if defined( GC )
|
||||
#include "tier0/memdbgoff.h"
|
||||
#endif
|
||||
|
||||
class CLock
|
||||
{
|
||||
#if defined( GC )
|
||||
DECLARE_CLASS_MEMPOOL( CLock );
|
||||
#endif
|
||||
public:
|
||||
CLock( );
|
||||
~CLock();
|
||||
|
||||
bool BIsLocked() const { return m_pJob != NULL; }
|
||||
CJob *GetJobLocking() { return m_pJob; }
|
||||
CJob *GetJobWaitingQueueHead() { return m_pJobToNotifyOnLockRelease; }
|
||||
CJob *GetJobWaitingQueueTail() { return m_pJobWaitingQueueTail; }
|
||||
void AddToWaitingQueue( CJob *pJob );
|
||||
|
||||
const char *GetName() const;
|
||||
void SetName( const char *pchName );
|
||||
|
||||
int16 GetLockType() const { return m_nsLockType; }
|
||||
void SetLockType( int16 nsLockType ) { m_nsLockType = nsLockType; }
|
||||
uint64 GetLockSubType() const { return m_unLockSubType; }
|
||||
void SetLockSubType( uint64 unLockSubType ) { m_unLockSubType = unLockSubType; }
|
||||
int32 GetWaitingCount() const { return m_nWaitingCount; }
|
||||
int64 GetMicroSecondsSinceLock() const { return m_sTimeAcquired.CServerMicroSecsPassed(); }
|
||||
void IncrementReference();
|
||||
int DecrementReference();
|
||||
void ClearReference() { m_nRefCount = 0; }
|
||||
int32 GetReferenceCount() const { return m_nRefCount; }
|
||||
|
||||
void Dump( const char *pszPrefix = "\t\t", int nPrintMax = 1, bool bPrintWaiting = true ) const;
|
||||
|
||||
private:
|
||||
CJob *m_pJob; // the job that's currently locking us
|
||||
CJob *m_pJobToNotifyOnLockRelease; // Pointer to the first job waiting on us
|
||||
CJob *m_pJobWaitingQueueTail; // Pointer to the last job waiting on us
|
||||
|
||||
const char *m_pchConstStr; // Prefix part of the lock's name
|
||||
|
||||
int32 m_nRefCount; // # of times locked
|
||||
int32 m_nWaitingCount; // Count of jobs waiting on the lock
|
||||
CJobTime m_sTimeAcquired; // Time the lock was last locked
|
||||
uint64 m_unLockSubType;
|
||||
|
||||
const char *m_pFilename; // Filename of the source file who acquired this lock
|
||||
int m_line; // Line number of the filename
|
||||
int16 m_nsLockType; // Lock priority for safely waiting on multiple locks
|
||||
|
||||
friend class CJob;
|
||||
};
|
||||
|
||||
#if defined( GC )
|
||||
#include "tier0/memdbgon.h"
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
// An auto lock class which handles auto lifetime management of a lock
|
||||
//-----------------------------------------------------------------------------------------
|
||||
class CGCAutoLock
|
||||
{
|
||||
public:
|
||||
CGCAutoLock() : m_pLock( NULL ) {}
|
||||
CGCAutoLock( const CGCAutoLock& rhs ) { Acquire( rhs.m_pLock ); }
|
||||
~CGCAutoLock() { Release(); }
|
||||
|
||||
//determines if this lock is currently locked or not
|
||||
bool IsLocked() const { return ( m_pLock != NULL ); }
|
||||
|
||||
//swaps two locks (faster than doing reassignments due to not needing all the reference counting)
|
||||
void Swap( CGCAutoLock& rhs ) { std::swap( m_pLock, rhs.m_pLock ); }
|
||||
|
||||
CGCAutoLock& operator=( const CGCAutoLock& rhs );
|
||||
|
||||
//called to acquire a lock (the odd naming convention is to match the macro format to automatically provide the file and line of the call site)
|
||||
bool _BYieldingAcquireLock( CLock& lock, const char* pszFile, uint32 nLine );
|
||||
|
||||
//called to release the current lock
|
||||
void Release();
|
||||
|
||||
private:
|
||||
void Acquire( CLock* pLock );
|
||||
CLock* m_pLock;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Use these macros to declare blocks where it is unsafe to yield.
|
||||
// The job will assert if it pauses within the block
|
||||
//-----------------------------------------------------------------------------
|
||||
#define DO_NOT_YIELD_THIS_SCOPE() CDoNotYieldScopeImpl doNotYieldScope_##line( FILE_AND_LINE )
|
||||
#define BEGIN_DO_NOT_YIELD() CDoNotYieldScopeImpl::InternalPush( FILE_AND_LINE )
|
||||
#define END_DO_NOT_YIELD() CDoNotYieldScopeImpl::InternalPop()
|
||||
|
||||
class CDoNotYieldScopeImpl
|
||||
{
|
||||
public:
|
||||
explicit CDoNotYieldScopeImpl( const char *pchLocation ) { InternalPush( pchLocation ); }
|
||||
~CDoNotYieldScopeImpl() { InternalPop(); }
|
||||
|
||||
static void InternalPush( const char *pchLocation );
|
||||
static void InternalPop();
|
||||
private:
|
||||
// Disallow these constructors and operators
|
||||
CDoNotYieldScopeImpl( const CDoNotYieldScopeImpl &that );
|
||||
CDoNotYieldScopeImpl &operator=( const CDoNotYieldScopeImpl &that );
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // GC_JOB_H
|
||||
462
public/gcsdk/jobmgr.h
Normal file
462
public/gcsdk/jobmgr.h
Normal file
@@ -0,0 +1,462 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GC_JOBMGR_H
|
||||
#define GC_JOBMGR_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/fasttimer.h"
|
||||
#include "tier1/utlpriorityqueue.h"
|
||||
#include "tier1/utlhashmaplarge.h"
|
||||
#include "tier1/utlpair.h"
|
||||
#include "job.h"
|
||||
#include "workthreadpool.h"
|
||||
class GCConVar;
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
#if defined(_DEBUG)
|
||||
// this is restricted to debug builds due to the performance cost
|
||||
// that could be changed by removing the expensive sm_listAllJobs.Find() command
|
||||
#define DEBUG_JOB_LIST
|
||||
#endif // defined(_DEBUG)
|
||||
|
||||
struct JobStats_t
|
||||
{
|
||||
uint m_cJobsCurrent;
|
||||
uint m_cJobsTotal;
|
||||
uint m_cJobsFailed;
|
||||
uint64 m_cJobsTimedOut; // # of jobs timed out ever
|
||||
double m_flSumJobTimeMicrosec;
|
||||
uint64 m_unMaxJobTimeMicrosec;
|
||||
|
||||
uint m_cTimeslices;
|
||||
|
||||
JobStats_t()
|
||||
{
|
||||
memset( this, 0, sizeof(JobStats_t) );
|
||||
}
|
||||
};
|
||||
|
||||
struct JobStatsBucket_t
|
||||
{
|
||||
JobStatsBucket_t()
|
||||
{
|
||||
memset( this, 0, sizeof(JobStatsBucket_t) );
|
||||
}
|
||||
char m_rgchName[64];
|
||||
uint64 m_cCompletes;
|
||||
uint64 m_u64RunTimeMax;
|
||||
uint64 m_cTimeoutNetMsg;
|
||||
uint64 m_cLongInterYieldTime;
|
||||
uint64 m_cLocksAttempted;
|
||||
uint64 m_cLocksWaitedFor;
|
||||
uint64 m_cLocksFailed;
|
||||
uint64 m_cLocksLongHeld;
|
||||
uint64 m_cLocksLongWait;
|
||||
uint64 m_cWaitTimeout;
|
||||
uint64 m_u64JobDuration;
|
||||
uint64 m_cJobsPaused;
|
||||
uint64 m_cJobsFailed;
|
||||
uint64 m_u64RunTime;
|
||||
// use by ListJobs
|
||||
uint64 m_cPauseReasonNetworkMsg;
|
||||
uint64 m_cPauseReasonSleepForTime;
|
||||
uint64 m_cPauseReasonWaitingForLock;
|
||||
uint64 m_cPauseReasonYield;
|
||||
uint64 m_cPauseReasonSQL;
|
||||
uint64 m_cPauseReasonWorkItem;
|
||||
};
|
||||
|
||||
enum EJobProfileAction
|
||||
{
|
||||
k_EJobProfileAction_ErrorReport = 0,
|
||||
k_EJobProfileAction_Start = 1,
|
||||
k_EJobProfileAction_Stop = 2,
|
||||
k_EJobProfileAction_Dump = 3,
|
||||
k_EJobProfileAction_Clear = 4,
|
||||
};
|
||||
|
||||
enum EJobProfileSortOrder
|
||||
{
|
||||
k_EJobProfileSortOrder_Alpha = 0,
|
||||
k_EJobProfileSortOrder_Count = 1,
|
||||
k_EJobProfileSortOrder_TotalRuntime = 2,
|
||||
};
|
||||
|
||||
//the various msg contexts that exist for the GC
|
||||
enum EGCMsgContext
|
||||
{
|
||||
k_eGCMsgContext_None = 0, // This message cannot be called by anyone. This is intended only to be used by messages that are temporarily disabled but left in the codebase for some reason
|
||||
k_eGCMsgContext_Client = (1<<0), // This message is expected to arrive from a player/client
|
||||
k_eGCMsgContext_UnsecuredServer = (1<<1), // This message should come from a dedicated server, but one that is not securely managed and should not be trusted
|
||||
k_eGCMsgContext_TrustedServer = (1<<2), // This message should come from a dedicated server, but one that is securely managed
|
||||
k_eGCMsgContext_System = (1<<3), // This message should come from the GCH system
|
||||
k_eGCMsgContext_OtherGC = (1<<4), // This message should arrive from another sub GC or the master GC
|
||||
|
||||
k_eGCMsgContext_AllServers = k_eGCMsgContext_UnsecuredServer | k_eGCMsgContext_TrustedServer, // a message from any server
|
||||
k_eGCMsgContext_All = k_eGCMsgContext_Client | k_eGCMsgContext_TrustedServer | k_eGCMsgContext_UnsecuredServer | k_eGCMsgContext_System | k_eGCMsgContext_OtherGC
|
||||
};
|
||||
|
||||
|
||||
struct JobProfileStats_t
|
||||
{
|
||||
int m_iJobProfileSort;
|
||||
CUtlHashMapLarge< uint32, JobStatsBucket_t > *pmapStatsBucket;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: This keeps track of all jobs that belong to a given hub.
|
||||
// It's primarily used for routing incoming messages to jobs.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CJobMgr
|
||||
{
|
||||
public:
|
||||
// Constructors & destructors
|
||||
CJobMgr();
|
||||
~CJobMgr();
|
||||
|
||||
// gets the next available job ID
|
||||
JobID_t GetNewJobID();
|
||||
|
||||
// Set the thread count for the internal thread pool(s)
|
||||
void SetThreadPoolSize( uint cThreads );
|
||||
|
||||
// Run any sleeping jobs who's wakeup time has arrived and check for timeouts
|
||||
bool BFrameFuncRunSleepingJobs( CLimitTimer &limitTimer );
|
||||
|
||||
// Run any yielding jobs, even low priority ones
|
||||
bool BFrameFuncRunYieldingJobs( CLimitTimer &limitTimer );
|
||||
|
||||
// Route this message to an existing Job, or create a new one if that JobID does not exist. This takes the context that the message
|
||||
// is being created within so that it can validate that the message is being called from the right context. If the context doesn't overlap,
|
||||
// it will fail to create the job
|
||||
bool BRouteMsgToJob( void *pParent, IMsgNetPacket *pNetPacket, const JobMsgInfo_t &jobMsgInfo, EGCMsgContext nCreateContext = k_eGCMsgContext_All );
|
||||
|
||||
// Adds a new Job to the mgr and generates a JobID for it.
|
||||
void InsertJob( CJob &job );
|
||||
|
||||
// Removes a Job from the mgr (the caller is still responsible for freeing it)
|
||||
void RemoveJob( CJob &job );
|
||||
|
||||
//called by a job that has just been started to place itself on the yield queue instead of running
|
||||
void AddDelayedJobToYieldList( CJob &job );
|
||||
|
||||
#ifdef GC
|
||||
|
||||
//given a message, this will determine if there is any custom routing associated with the message. It will return a function pointer
|
||||
//if custom routing is found, or NULL otherwise
|
||||
JobRoutingFunc_t GetRoutingForMsg( IMsgNetPacket *pNetPacket );
|
||||
JobRoutingFunc_t GetRoutingForMsg( MsgType_t eMsg );
|
||||
|
||||
// resumes the specified job if it is, in fact, waiting for a SQL query to return
|
||||
bool BResumeSQLJob( JobID_t jobID );
|
||||
|
||||
// yields waiting for a query response
|
||||
bool BYieldingRunQuery( CJob &job, CGCSQLQueryGroup *pQueryGroup, ESchemaCatalog eSchemaCatalog );
|
||||
|
||||
//stats collected for SQL queries that run long
|
||||
struct LongSQLStats_t
|
||||
{
|
||||
//the string for this query
|
||||
CUtlString m_sQuery;
|
||||
//total amount of time spent in over limit queries
|
||||
uint32 m_nTotalTimeMS;
|
||||
uint32 m_nNumQueries;
|
||||
//the parameters for the longest running query
|
||||
uint32 m_nMaxQueryMS;
|
||||
CUtlString m_sMaxQueryParams;
|
||||
//a collection of N parameters that also exceeded the time limit
|
||||
static const uint32 knMaxParams = 8;
|
||||
CUtlVectorFixed< CUtlString, knMaxParams > m_Params;
|
||||
};
|
||||
|
||||
//access to the mapfor all the long running queries
|
||||
typedef CUtlHashMapLarge< const char*, LongSQLStats_t*, CaseSensitiveStrEquals, MurmurHash3ConstCharPtr > TLongSQLMap;
|
||||
TLongSQLMap& GetLongSQLMap() { return m_LongSqlMap; }
|
||||
|
||||
|
||||
// SQL profiling
|
||||
enum ESQLProfileSort
|
||||
{
|
||||
k_ESQLProfileSortTotalTime,
|
||||
k_ESQLProfileSortTotalCount,
|
||||
k_ESQLProfileSortAvgTime,
|
||||
k_ESQLProfileSortName
|
||||
};
|
||||
|
||||
void StartSQLProfiling();
|
||||
void StopSQLProfiling();
|
||||
void DumpSQLProfile( ESQLProfileSort eSort );
|
||||
uint32 GetNumSQLQueriesInFlight() const { return m_mapSQLQueriesInFlight.Count(); }
|
||||
#endif
|
||||
|
||||
// returns true if we're running any jobs of the specified name
|
||||
// slow to call if lots of jobs are running, should only be used by tests
|
||||
bool BIsJobRunning( const char *pchJobName );
|
||||
|
||||
// passes a network msg directly to the specified job
|
||||
void PassMsgToJob( CJob &job, IMsgNetPacket *pNetPacket, const JobMsgInfo_t &jobMsgInfo );
|
||||
|
||||
// Enter an advisory do-not-yield scope with line-and-file string for debugging
|
||||
void PushDoNotYield( CJob &job, const char *pchFileAndLine );
|
||||
|
||||
// Exit the innermost do-not-yield scope which matches this job
|
||||
void PopDoNotYield( CJob &job );
|
||||
|
||||
// yields until a network message is received
|
||||
bool BYieldingWaitForMsg( CJob &job );
|
||||
|
||||
// yields for a set amount of time
|
||||
bool BYieldingWaitTime( CJob &job, uint32 cMicrosecondsToSleep );
|
||||
|
||||
// simple yield until Run() called again
|
||||
bool BYield( CJob &job );
|
||||
|
||||
// Yield only if job manager decides we need to
|
||||
bool BYieldIfNeeded( CJob &job, bool *pbYielded );
|
||||
|
||||
// Thread pool work item
|
||||
bool BYieldingWaitForWorkItem( CJob &job, const char *pszWorkItemName = NULL );
|
||||
bool BRouteWorkItemCompleted( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ true, /* bResumeImmediately */ true ); }
|
||||
bool BRouteWorkItemCompletedIfExists( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ false, /* bResumeImmediately */ true ); }
|
||||
bool BRouteWorkItemCompletedDelayed( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ true, /* bResumeImmediately */ false ); }
|
||||
bool BRouteWorkItemCompletedIfExistsDelayed( JobID_t jobID, bool bWorkItemCanceled ) { return BRouteWorkItemCompletedInternal( jobID, bWorkItemCanceled, /* bShouldExist */ false, /* bResumeImmediately */ false ); }
|
||||
|
||||
void AddThreadedJobWorkItem( CWorkItem *pWorkItem );
|
||||
void StopWorkThreads() { m_WorkThreadPool.StopWorkThreads(); }
|
||||
|
||||
static int ProfileSortFunc( void *pCtx, const int *lhs, const int *rhs );
|
||||
|
||||
void ProfileJobs( EJobProfileAction ejobProfileAction, EJobProfileSortOrder iSortOrder = k_EJobProfileSortOrder_Alpha );
|
||||
int DumpJobSummary();
|
||||
void DumpJob( JobID_t jobID, int nPrintLocksMax = 20 ) const;
|
||||
int CountJobs() const; // counts currently active jobs
|
||||
void CheckThreadID(); // make sure we are still in the correct thread
|
||||
int CountYieldingJobs() const { return m_ListJobsYieldingRegPri.Count(); } // counts jobs currently in a yielding state
|
||||
bool HasOutstandingThreadPoolWorkItems();
|
||||
|
||||
void SetIsShuttingDown( bool bIsShuttingDown ) { m_bIsShuttingDown = bIsShuttingDown; }
|
||||
bool GetIsShuttingDown() const { return m_bIsShuttingDown; }
|
||||
|
||||
void *GetMainMemoryDebugInfo() { return g_memMainDebugInfo.Base(); }
|
||||
|
||||
// wakes up a job that was waiting on a lock
|
||||
void WakeupLockedJob( CJob &job );
|
||||
|
||||
// returns true if there is a job active with the specified ID
|
||||
bool BJobExists( JobID_t jobID ) const;
|
||||
|
||||
// returns a job
|
||||
CJob *GetPJob( JobID_t jobID );
|
||||
const CJob *GetPJob( JobID_t jobID ) const;
|
||||
|
||||
JobStats_t& GetJobStats() { return m_JobStats; }
|
||||
|
||||
// Access work thread pool directly
|
||||
CWorkThreadPool *AccessWorkThreadPool() { return &m_WorkThreadPool; }
|
||||
|
||||
// Debug helpers
|
||||
// dumps a list of all running jobs across ALL job managers
|
||||
void DumpJobs( const char *pszJobName, int nMax, int nPrintLocksMax = 1 ) const;
|
||||
// cause a debug break in the given job
|
||||
static void DebugJob( int iJob );
|
||||
|
||||
//sets a whitelist of messages that should be exempt from context filtering. This is meant only to be a utility during development to address
|
||||
//messages that were misclassified until a fix can be deployed
|
||||
void SetMsgContextWhitelist( const CUtlVector< MsgType_t >& msgList ) { m_MsgContextWhitelist = msgList; }
|
||||
|
||||
private:
|
||||
|
||||
//given a message, this will find the appropriate job type structure for handling this message
|
||||
const JobType_t* GetJobInfoForMsg( IMsgNetPacket *pNetPacket ) const;
|
||||
|
||||
bool BRouteWorkItemCompletedInternal( JobID_t jobID, bool bWorkItemCanceled, bool bShouldExist, bool bResumeImmediately );
|
||||
|
||||
// Create a new job for this message
|
||||
bool BLaunchJobFromNetworkMsg( void *pParent, const JobMsgInfo_t &jobMsgInfo, IMsgNetPacket *pNetPacket, EGCMsgContext nCreateContext );
|
||||
|
||||
// Internal add to yield list (looks at priority)
|
||||
void AddToYieldList( CJob &job );
|
||||
|
||||
// Get an IJob given a job ID and pause reason
|
||||
bool BGetIJob( JobID_t jobID, EJobPauseReason eJobPauseReason, bool bShouldExist, int *pIJob );
|
||||
|
||||
// Map containing all of our jobs
|
||||
CUtlHashMapLarge<JobID_t, CJob *> m_MapJob;
|
||||
|
||||
// jobs simply waiting until the next Run()
|
||||
struct JobYielding_t
|
||||
{
|
||||
JobID_t m_JobID;
|
||||
uint m_nIteration;
|
||||
};
|
||||
CUtlLinkedList<JobYielding_t, int> m_ListJobsYieldingRegPri;
|
||||
bool BResumeYieldingJobs( CLimitTimer &limitTimer );
|
||||
bool BResumeYieldingJobsFromList( CUtlLinkedList<JobYielding_t, int> &listJobsYielding, uint nCurrentIteration, CLimitTimer &limitTimer );
|
||||
uint m_nCurrentYieldIterationRegPri;
|
||||
|
||||
// jobs waiting on a timer
|
||||
struct JobSleeping_t
|
||||
{
|
||||
JobID_t m_JobID;
|
||||
CJobTime m_SWakeupTime;
|
||||
CJobTime m_STimeTouched;
|
||||
};
|
||||
CUtlPriorityQueue<JobSleeping_t> m_QueueJobSleeping;
|
||||
bool BResumeSleepingJobs( CLimitTimer &limitTimer );
|
||||
static bool JobSleepingLessFunc( JobSleeping_t const &lhs, JobSleeping_t const &rhs );
|
||||
|
||||
// timeout list of jobs, ordered from oldest to newest
|
||||
struct JobTimeout_t
|
||||
{
|
||||
JobID_t m_JobID;
|
||||
CJobTime m_STimePaused;
|
||||
CJobTime m_STimeTouched;
|
||||
uint32 m_cHeartbeatsBeforeTimeout;
|
||||
};
|
||||
CUtlLinkedList<JobTimeout_t, int> m_ListJobTimeouts;
|
||||
CUtlHashMapLarge< JobID_t, int > m_MapJobTimeoutsIndexByJobID;
|
||||
void PauseJob( CJob &job, EJobPauseReason eJobPauseReason, const char *pszPauseResourceName = NULL );
|
||||
void CheckForJobTimeouts( CLimitTimer &limitTimer );
|
||||
void TimeoutJob( CJob &job );
|
||||
bool m_bJobTimedOut;
|
||||
|
||||
// thread pool usage, for running job functions in other threads
|
||||
CWorkThreadPool m_WorkThreadPool;
|
||||
|
||||
void AccumulateStatsofJob( CJob &job );
|
||||
void RecordOrphanedMessage( MsgType_t eMsg, JobID_t jobIDTarget );
|
||||
|
||||
// stats info
|
||||
JobStats_t m_JobStats;
|
||||
|
||||
// static job registration
|
||||
static void RegisterJobType( const JobType_t *pJobType );
|
||||
friend void Job_RegisterJobType( const JobType_t *pJobType );
|
||||
|
||||
JobID_t m_unNextJobID;
|
||||
uint m_unFrameFuncThreadID; // the thread is JobMgr is working in
|
||||
bool m_bProfiling;
|
||||
bool m_bIsShuttingDown;
|
||||
int m_cErrorsToReport;
|
||||
CUtlHashMapLarge< uint32, JobStatsBucket_t > m_mapStatsBucket;
|
||||
CUtlHashMapLarge< MsgType_t, int > m_mapOrphanMessages;
|
||||
CUtlMemory<unsigned char> g_memMainDebugInfo;
|
||||
|
||||
//a white list of messages to ignore context issues with
|
||||
CUtlVector< MsgType_t > m_MsgContextWhitelist;
|
||||
|
||||
#ifdef GC
|
||||
// sql profiling
|
||||
bool m_bSQLProfiling;
|
||||
CFastTimer m_sqlTimer;
|
||||
|
||||
// long SQL utilities
|
||||
TLongSQLMap m_LongSqlMap;
|
||||
|
||||
struct PendingSQLJob_t
|
||||
{
|
||||
uint64 m_nStartMicrosec;
|
||||
CGCSQLQueryGroup *m_pQueryGroup;
|
||||
int32 m_iBucket;
|
||||
};
|
||||
|
||||
//utility function that given a pending SQL query will handle displaying it out to the console along with all of its parameters. Useful
|
||||
//for debugging and monitoring
|
||||
void DisplayPendingSQLJob( const PendingSQLJob_t& sqlJob );
|
||||
|
||||
struct SQLProfileBucket_t
|
||||
{
|
||||
uint64 m_nTotalMicrosec;
|
||||
uint32 m_unCount;
|
||||
uint32 m_unMaxMS;
|
||||
};
|
||||
|
||||
CUtlHashMapLarge<GID_t, PendingSQLJob_t> m_mapSQLQueriesInFlight;
|
||||
CUtlDict<SQLProfileBucket_t> m_dictSQLBuckets;
|
||||
|
||||
struct SQLProfileCtx_t
|
||||
{
|
||||
ESQLProfileSort m_eSort;
|
||||
CUtlDict<SQLProfileBucket_t> *pdictBuckets;
|
||||
};
|
||||
static int SQLProfileSortFunc( void *pCtx, const int *lhs, const int *rhs );
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_JOB_LIST
|
||||
// static job debug list
|
||||
static CUtlLinkedList<CJob *, int> sm_listAllJobs;
|
||||
#endif
|
||||
|
||||
// A stack of do not yield guards so we can print the right warning if they're nested
|
||||
// (Note that we also track jobid since it is not an error to start another job which
|
||||
// executes and immediately yields back to the parent, even if the parent can't yield)
|
||||
CUtlVector< CUtlPair< JobID_t, const char * > > m_stackDoNotYieldGuards;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: passthrough function just so the CJob internal data can be kept private
|
||||
//-----------------------------------------------------------------------------
|
||||
inline void Job_RegisterJobType( const JobType_t *pJobType )
|
||||
{
|
||||
CJobMgr::RegisterJobType( pJobType );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: passthrough function just so the CJob internal data can be kept private
|
||||
//-----------------------------------------------------------------------------
|
||||
inline void Job_SetJobType( CJob &job, const JobType_t *pJobType )
|
||||
{
|
||||
job.m_pJobType = pJobType;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: job registration macro
|
||||
//-----------------------------------------------------------------------------
|
||||
#define GC_REG_JOB_FULL( parentclass, jobclass, jobname, msg, contexts, routingfn, consolevar ) \
|
||||
GCSDK::CJob *CreateJob_##jobclass( parentclass *pParent, void * pvStartParam ); \
|
||||
static const GCSDK::JobType_t g_JobType_##jobclass = { jobname, (GCSDK::MsgType_t)msg, contexts, (GCSDK::JobCreationFunc_t)CreateJob_##jobclass, routingfn, consolevar }; \
|
||||
GCSDK::CJob *CreateJob_##jobclass( parentclass *pParent, void * pvStartParam ) \
|
||||
{ \
|
||||
GCSDK::CJob *job = GCSDK::CJob::AllocateJob<jobclass>( pParent ); \
|
||||
if ( job ) \
|
||||
{ \
|
||||
Job_SetJobType( *job, &g_JobType_##jobclass ); \
|
||||
if ( pvStartParam ) job->SetStartParam( pvStartParam ); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
AssertMsg( job, "CJob::AllocateJob<" #jobclass "> returned NULL!" ); \
|
||||
} \
|
||||
return job; \
|
||||
} \
|
||||
static class CRegJob_##jobclass \
|
||||
{ \
|
||||
public: CRegJob_##jobclass() \
|
||||
{ \
|
||||
Job_RegisterJobType( &g_JobType_##jobclass ); \
|
||||
} \
|
||||
} g_RegJob_##jobclass;
|
||||
|
||||
#define GC_REG_JOB( parentclass, jobclass, jobname, msg ) \
|
||||
GC_REG_JOB_FULL( parentclass, jobclass, jobname, msg, GCSDK::k_eGCMsgContext_All, NULL, NULL )
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // GC_JOBMGR_H
|
||||
60
public/gcsdk/jobtime.h
Normal file
60
public/gcsdk/jobtime.h
Normal file
@@ -0,0 +1,60 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Encapsultes the job system's version of time (which is != to wall clock time)
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef JOBTIME_H
|
||||
#define JOBTIME_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
// CJobTime
|
||||
// This is our primary job time structure. It's similar to the Windows FILETIME structure, but
|
||||
// with 1/10th the nominal resolution.
|
||||
// It offers 1 microsecond resolution beginning on January 1, 1601.
|
||||
// This is NOT wall clock time, it is time based proxy for a frame counter.
|
||||
// This time PAUSES when you are in the debugger. Most timeout checks (things that don't need to be
|
||||
// exact time wise should use this );
|
||||
class CJobTime
|
||||
{
|
||||
public:
|
||||
CJobTime();
|
||||
|
||||
void SetToJobTime();
|
||||
void SetFromJobTime( int64 dMicroSecOffset );
|
||||
|
||||
// Amount of time that's passed between this CJobTime and the current time.
|
||||
int64 CServerMicroSecsPassed() const;
|
||||
|
||||
// Time accessors
|
||||
uint64 LTime() const { return m_lTime; }
|
||||
void SetLTime( uint64 lTime ) { m_lTime = lTime; }
|
||||
|
||||
// Access our cached current time value
|
||||
static void UpdateJobTime( int cMicroSecPerShellFrame );
|
||||
static void SetCurrentJobTime( uint64 lCurrentTime );
|
||||
static uint64 LJobTimeCur() { return sm_lTimeCur; }
|
||||
|
||||
bool operator==( const CJobTime &val ) const { return val.m_lTime == m_lTime; }
|
||||
bool operator!=( const CJobTime &val ) const { return val.m_lTime != m_lTime; }
|
||||
bool operator<( const CJobTime &val ) const { return m_lTime < val.m_lTime; }
|
||||
bool operator>( const CJobTime &val ) const { return m_lTime > val.m_lTime; }
|
||||
const CJobTime& operator+( const int64 &val ) { m_lTime += val; return *this; }
|
||||
const CJobTime& operator+=( const int64 &val ) { m_lTime += val; return *this; }
|
||||
|
||||
private:
|
||||
|
||||
uint64 m_lTime; // Our time value (microseconds since 1/1/1601)
|
||||
static uint64 sm_lTimeCur; // Cached value of the current time (updated each frame)
|
||||
};
|
||||
|
||||
const uint64 k_lJobTimeMaxFuture = (uint64)-1;
|
||||
|
||||
|
||||
}
|
||||
#endif // JOBTIME_H
|
||||
170
public/gcsdk/messagelist.h
Normal file
170
public/gcsdk/messagelist.h
Normal file
@@ -0,0 +1,170 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Maps message types to strings and vice versa
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef MESSAGELIST_H
|
||||
#define MESSAGELIST_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "google/protobuf/descriptor.h"
|
||||
#include "gcsdk/jobtime.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
extern CGCEmitGroup g_EGMessages;
|
||||
extern const char *PchMsgNameFromEMsg( MsgType_t eMsg );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// message type flags
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static const int MT_GC = 0x01; // this message is sent to or from a Game Coordinator (will be proxied by servers along the way)
|
||||
static const int MT_GC_SYSTEM = 0x02; // this message was sent to or from the steam servers as a system message. Clients can't send these
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Various info about each message type
|
||||
//-----------------------------------------------------------------------------
|
||||
struct MsgInfo_t
|
||||
{
|
||||
MsgType_t eMsg;
|
||||
int nFlags;
|
||||
const char *pchMsgName;
|
||||
|
||||
struct Stats_t
|
||||
{
|
||||
Stats_t() : nSourceMask(0), nCount( 0 ), uBytes( 0 ) {}
|
||||
uint32 nSourceMask;
|
||||
uint32 nCount;
|
||||
uint64 uBytes;
|
||||
};
|
||||
|
||||
enum EStatsType
|
||||
{
|
||||
k_EStatsTypeSent,
|
||||
k_EStatsTypeReceived,
|
||||
k_EStatsTypeMultiplexedSends,
|
||||
k_EStatsTypeMultiplexedSendsRaw,
|
||||
|
||||
k_EStatsType_Count
|
||||
};
|
||||
|
||||
enum EStatsGroup
|
||||
{
|
||||
k_EStatsGroupGlobal,
|
||||
k_EStatsGroupProfile,
|
||||
k_EStatsGroupWindow,
|
||||
|
||||
k_EStatsGroup_Count
|
||||
};
|
||||
|
||||
|
||||
Stats_t stats[ k_EStatsGroup_Count ][ k_EStatsType_Count ];
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Using protobuf reflection, bind them into a message list
|
||||
//-----------------------------------------------------------------------------
|
||||
void MsgRegistrationFromEnumDescriptor( const ::google::protobuf::EnumDescriptor *pEnumDescriptor, int nTypeMask );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// manages a hashed list of messages, allowing fast tests for validity and
|
||||
// info lookup.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CMessageList
|
||||
{
|
||||
public:
|
||||
CMessageList();
|
||||
~CMessageList();
|
||||
|
||||
bool BInit( );
|
||||
|
||||
// returns false if a message isn't valid or isn't one of the types specified
|
||||
// or true if the message is valid. ppMsgName can be NULL.
|
||||
bool GetMessage( MsgType_t eMsg, const char **ppMsgName, int nTypeMask );
|
||||
|
||||
// make stats about sending messages
|
||||
void TallySendMessage( MsgType_t eMsg, uint32 uMsgSize, uint32 nSourceMask = 0 );
|
||||
void TallyReceiveMessage( MsgType_t eMsg, uint32 uMsgSize, uint32 nSourceMask = 0);
|
||||
void TallyMultiplexedMessage( MsgType_t eMsg, uint32 uSent, uint32 cRecipients, uint32 uMsgSize, uint32 nSourceMask = 0 );
|
||||
|
||||
// profiling
|
||||
void EnableProfiling( bool bEnableProfiling );
|
||||
|
||||
// print out our stats
|
||||
void PrintStats( bool bShowAll, bool bSortByFrequency, MsgInfo_t::EStatsGroup eGroup, MsgInfo_t::EStatsType eType, uint32 nSourceMask = 0 ) const;
|
||||
void PrintMultiplexStats( MsgInfo_t::EStatsGroup eGroup, bool bSortByFrequency, uint32 nSourceMask = 0 ) const;
|
||||
|
||||
// Window management - This is similar to profiling in many ways, but is separate so that the base system can monitor traffic rates without
|
||||
// interfering with profiles.
|
||||
|
||||
//called to obtain the totals for the timing window
|
||||
const MsgInfo_t::Stats_t& GetWindowTotal( MsgInfo_t::EStatsType eType ) const;
|
||||
//called to reset the window timings
|
||||
void ResetWindow();
|
||||
//returns how long this window has been running in microseconds
|
||||
uint64 GetWindowDuration() const { return GetGroupDuration( MsgInfo_t::k_EStatsGroupWindow ); }
|
||||
|
||||
|
||||
private:
|
||||
void TallyMessageInternal( MsgInfo_t &msgInfo, MsgInfo_t::EStatsType eBucket, uint32 unMsgSize, uint32 nSourceMask, uint32 cMessages = 1 );
|
||||
|
||||
void AssureBucket( int nBucket );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// given a particular message ID, find out what bucket it would be in,
|
||||
// as well as which slot in that bucket.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int HashMessage( MsgType_t eMsg, int &nSlot )
|
||||
{
|
||||
// hash is everything except the lowest nibble,
|
||||
// because buckets are 16 entries
|
||||
int nBucket = eMsg / m_kcBucketSize;
|
||||
nSlot = eMsg % m_kcBucketSize;
|
||||
return nBucket;
|
||||
}
|
||||
|
||||
short GetMessageIndex( MsgType_t eMsg );
|
||||
|
||||
//given a group, this will return the time that the stats have been collected over
|
||||
uint64 GetGroupDuration( MsgInfo_t::EStatsGroup eGroup ) const;
|
||||
|
||||
private:
|
||||
|
||||
//totalled stats for the current window. It would be too costly to total these all the time
|
||||
MsgInfo_t::Stats_t m_WindowTotals[ MsgInfo_t::k_EStatsType_Count ];
|
||||
|
||||
//the time that we have been collecting each of these buckets
|
||||
CJobTime m_sCollectTime[ MsgInfo_t::k_EStatsGroup_Count ];
|
||||
|
||||
//are we currently actively tracking a profile?
|
||||
bool m_bProfiling;
|
||||
//the duration of the last finished profile (if it is no longer running, otherwise use the timer)
|
||||
uint64 m_ulProfileMicrosecs;
|
||||
|
||||
CUtlVector< short* > m_vecMessageInfoBuckets;
|
||||
CUtlVector<MsgInfo_t> m_vecMsgInfo;
|
||||
static const int m_kcBucketSize = 16;
|
||||
};
|
||||
|
||||
extern CMessageList g_theMessageList;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns the true if the specified message is a valid GC system message.
|
||||
// Input : eMsg - message type to test
|
||||
// ppMsgName - Optional pointer to receive message name
|
||||
//-----------------------------------------------------------------------------
|
||||
inline bool BIsValidSystemMsg( MsgType_t eMsg, const char **ppMsgName )
|
||||
{
|
||||
return g_theMessageList.GetMessage( eMsg, ppMsgName, MT_GC_SYSTEM );
|
||||
}
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // MESSAGELIST_H
|
||||
870
public/gcsdk/msgbase.h
Normal file
870
public/gcsdk/msgbase.h
Normal file
@@ -0,0 +1,870 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Holds the CMsgBase_t class
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCMSGBASE_H
|
||||
#define GCMSGBASE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier1/tsmultimempool.h"
|
||||
#include "gclogger.h"
|
||||
#include "gcconstants.h"
|
||||
#include "refcount.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CNetPacket;
|
||||
|
||||
// used for message types in GCSDK where we don't have the actual enum
|
||||
typedef uint32 MsgType_t;
|
||||
const uint32 k_EMsgProtoBufFlag = 0x80000000;
|
||||
|
||||
//extern ConVar g_ConVarMsgErrorDump;
|
||||
extern CThreadSafeMultiMemoryPool g_MemPoolMsg;
|
||||
|
||||
enum EMsgFormatType
|
||||
{
|
||||
k_EMsgFormatTypeStruct = 0,
|
||||
k_EMsgFormatTypeClientStruct = 1,
|
||||
k_EMsgFormatTypeClientStructDeprecated = 2,
|
||||
k_EMsgFormatTypeProtocolBuffer = 3
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Interface that the CNetPacket wrappers for both old/new inter-server
|
||||
// message formats must implement.
|
||||
//
|
||||
class IMsgNetPacket : public CRefCount
|
||||
{
|
||||
public:
|
||||
|
||||
virtual EMsgFormatType GetEMsgFormatType() const = 0;
|
||||
|
||||
virtual CNetPacket *GetCNetPacket() const = 0;
|
||||
virtual uint8 *PubData() const = 0;
|
||||
virtual uint CubData() const = 0;
|
||||
|
||||
//
|
||||
// Inter-server or client messages in both old/new formats
|
||||
//
|
||||
virtual MsgType_t GetEMsg() const = 0;
|
||||
virtual JobID_t GetSourceJobID() const = 0;
|
||||
virtual JobID_t GetTargetJobID() const = 0;
|
||||
virtual void SetTargetJobID( JobID_t ulJobID ) = 0;
|
||||
|
||||
//
|
||||
// Client messages only in the old format, optional in any msg in the new format
|
||||
//
|
||||
virtual CSteamID GetSteamID() const = 0;
|
||||
virtual void SetSteamID( CSteamID steamID ) = 0;
|
||||
|
||||
// Inter-gc messages only
|
||||
virtual AppId_t GetSourceAppID() const = 0;
|
||||
virtual void SetSourceAppID( AppId_t appId ) = 0;
|
||||
|
||||
//
|
||||
// The name of the job type to route this message to. ProtoBuf messages only
|
||||
//
|
||||
virtual bool BHasTargetJobName() const = 0;
|
||||
virtual const char *GetTargetJobName() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
// Needed due to CRefCount inheritance which makes this not a pure interface class
|
||||
virtual ~IMsgNetPacket() {}
|
||||
|
||||
};
|
||||
|
||||
IMsgNetPacket *IMsgNetPacketFromCNetPacket( CNetPacket *pNetPacket );
|
||||
|
||||
|
||||
// Wrapper around IMsgNetPacket which auto-releases
|
||||
class CIMsgNetPacketAutoRelease
|
||||
{
|
||||
public:
|
||||
CIMsgNetPacketAutoRelease( CNetPacket *pNetPacket ) { m_pMsgNetPacket = IMsgNetPacketFromCNetPacket( pNetPacket ); }
|
||||
~CIMsgNetPacketAutoRelease() { SAFE_RELEASE( m_pMsgNetPacket ); }
|
||||
|
||||
void Replace( CNetPacket *pNetPacket, bool bIsClientMsg ) { SAFE_RELEASE( m_pMsgNetPacket ); m_pMsgNetPacket = IMsgNetPacketFromCNetPacket( pNetPacket ); }
|
||||
|
||||
IMsgNetPacket *Get() { return m_pMsgNetPacket; }
|
||||
IMsgNetPacket *operator->() { return m_pMsgNetPacket; }
|
||||
|
||||
protected:
|
||||
void operator=( const CIMsgNetPacketAutoRelease &that ) { AssertMsg( false, "Not safe to copy since releases references on destruction" ); }
|
||||
CIMsgNetPacketAutoRelease( const CIMsgNetPacketAutoRelease &that ) { AssertMsg( false, "Not safe to copy since releases references on destruction" ); }
|
||||
|
||||
IMsgNetPacket *m_pMsgNetPacket;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Helper class for incoming and outgoing network packets.
|
||||
// IMPORTANT: Note the distinction between pubData and cubData
|
||||
// (which refer to the message payload), and pubMsg and cubMsg
|
||||
// (which refer to the entire message, with the header).
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
class CMsgBase_t
|
||||
{
|
||||
public:
|
||||
// Send constructor
|
||||
CMsgBase_t( uint32 cubStruct, uint32 cubReserve = 0 );
|
||||
|
||||
// copies data from pubPkt
|
||||
CMsgBase_t( const uint8 *pubPkt, uint32 cubPkt );
|
||||
|
||||
// Receive constructor - aliases pubPkt
|
||||
CMsgBase_t( uint32 cubHdr, uint32 cubStruct, uint8 *pubPkt, uint32 cubPkt/*, HCONNECTION hConnection = NULL*/ );
|
||||
|
||||
// set packet after using empty constructor
|
||||
void SetPacket( IMsgNetPacket *pNetPacket );
|
||||
|
||||
// Destructor
|
||||
virtual ~CMsgBase_t();
|
||||
|
||||
// Accessors
|
||||
uint8 *PubVarData() { return ( m_pubPkt + m_cubMsgHdr + m_cubStruct ); }
|
||||
const uint8 *PubVarData() const { return ( m_pubPkt + m_cubMsgHdr + m_cubStruct ); }
|
||||
uint32 CubVarData() const
|
||||
{
|
||||
if ( m_cubPkt >= ( m_cubMsgHdr + m_cubStruct ) )
|
||||
return m_cubPkt - m_cubMsgHdr - m_cubStruct;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
uint8 *PubPkt() { return m_pubPkt; }
|
||||
const uint8 *PubPkt() const { return m_pubPkt; }
|
||||
uint32 CubPkt() const { return m_cubPkt; }
|
||||
MSG_HEADER_TYPE &Hdr() { return * ( MSG_HEADER_TYPE * ) ( m_pubPkt ); }
|
||||
const MSG_HEADER_TYPE &Hdr() const { return * ( MSG_HEADER_TYPE * ) ( m_pubPkt ); }
|
||||
uint32 CubHdr() const { return m_cubMsgHdr; }
|
||||
|
||||
uint8* PubBody() { return m_pubBody; }
|
||||
const uint8* PubBody() const { return m_pubBody; }
|
||||
uint32 CubBody() const { return CubPkt() - CubHdr(); }
|
||||
|
||||
// Add additional data
|
||||
int DubWriteCur() { return m_cubPkt - m_cubMsgHdr - m_cubStruct; } // Our current offset within the var data block
|
||||
void AddBoolData( bool bData );
|
||||
void AddUint8Data( uint8 ubData );
|
||||
void AddUintData( uint32 unData );
|
||||
void AddIntData( int32 nData );
|
||||
void AddInt16Data( int16 sData );
|
||||
void AddUint16Data( uint16 usData );
|
||||
void AddUint64Data( uint64 ulData );
|
||||
void AddInt64Data( int64 lData );
|
||||
void AddFloatData( float lData );
|
||||
void AddVariableLenData( const void *pvData, uint cubLen );
|
||||
void AddStrData( const char *pchIn );
|
||||
template<typename T> void AddStructure(T& structure ) ;
|
||||
|
||||
// Read variable-length data (can also read manually using PubVarData(), CubVarData()
|
||||
void ResetReadPtr() { m_pubVarRead = PubVarData(); }
|
||||
uint8 *PubReadCur() { return m_pubVarRead; }
|
||||
const uint8 *PubReadCur() const { return m_pubVarRead; }
|
||||
uint32 CubReadRemaining() const { return (uint32)(m_pubPkt + m_cubPkt - m_pubVarRead); }
|
||||
void AdvanceReadPtr( int cubSkip ) { m_pubVarRead += cubSkip; }
|
||||
bool BReadBoolData( bool *pbData );
|
||||
bool BReadUint8Data( uint8 *pbData );
|
||||
bool BReadUintData( uint32 *punData );
|
||||
bool BReadIntData( int32 *pnData );
|
||||
bool BReadInt16Data( int16 *psData );
|
||||
bool BReadUint16Data( uint16 *pusData );
|
||||
bool BReadUint64Data( uint64 *pulData );
|
||||
bool BReadInt64Data( int64 *plData );
|
||||
bool BReadFloatData( float *pflData );
|
||||
bool BReadVariableLenData( void *pvBuff, uint32 cubRead );
|
||||
bool BReadStr( char *pchBuff, int cchBuff );
|
||||
bool BReadStr( CUtlString *pstr );
|
||||
template<typename T> bool BReadStructure(T& structure) ;
|
||||
|
||||
// returns pointer to data (and size of data in out ptr), and NULLs out own pointer;
|
||||
// caller now owns this memory and must free
|
||||
uint8 * DetachPkt( int * pcubPkt )
|
||||
{
|
||||
Assert( pcubPkt );
|
||||
*pcubPkt = m_cubPkt;
|
||||
uint8 * pRetVal = m_pubPkt;
|
||||
m_pubPkt = NULL;
|
||||
m_pubBody = NULL;
|
||||
m_cubPkt = 0;
|
||||
return pRetVal;
|
||||
}
|
||||
|
||||
void ResetWritePtr()
|
||||
{
|
||||
m_cubPkt = m_cubMsgHdr + m_cubStruct;
|
||||
}
|
||||
|
||||
uint32 GetWriteOffset() const { return m_cubPkt; }
|
||||
void SetWriteOffset( uint32 nWriteOffset ) { m_cubPkt = nWriteOffset; }
|
||||
|
||||
// Called to set the JobID that will be expecting
|
||||
// a reply to this message.
|
||||
void ExpectingReply( JobID_t jobIDSource )
|
||||
{
|
||||
Hdr().m_JobIDSource = jobIDSource;
|
||||
}
|
||||
|
||||
bool BIsExpectingReply() const { return Hdr().m_JobIDSource != k_GIDNil; }
|
||||
|
||||
// make sure the buffer can hold this extra amount of data
|
||||
void EnsurePacketSize( uint32 cubNewsize );
|
||||
//HCONNECTION GetHConnection() const { return m_hConnection; }
|
||||
|
||||
void ReportBufferOverflow();
|
||||
void PacketDump(); // spews complete packet content to console
|
||||
|
||||
protected:
|
||||
// Shared by send & receive
|
||||
uint8 *m_pubPkt; // Raw packet data
|
||||
uint8 *m_pubBody; // pointer to body; always equal to m_pubPkt + m_cubMsgHdr
|
||||
uint32 m_cubPkt; // Raw packet size
|
||||
const uint32 m_cubMsgHdr; // Size of our message header
|
||||
uint32 m_cubStruct; // Size of our message-specific struct
|
||||
//HCONNECTION m_hConnection; // Connection on which we received the message
|
||||
private:
|
||||
// stop people from hurting themselves
|
||||
CMsgBase_t( const CMsgBase_t &rhs ) {};
|
||||
CMsgBase_t &operator=( const CMsgBase_t &rhs ) {};
|
||||
|
||||
bool m_bAlloced; // Did we allocate this buffer or does someone else own it
|
||||
|
||||
// Receive only
|
||||
uint8 *m_pubVarRead; // Our current read pointer in the variable-length data
|
||||
};
|
||||
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
CMsgBase_t<MSG_HEADER_TYPE>::CMsgBase_t( uint32 cubStruct, uint32 cubReserve )
|
||||
: m_cubMsgHdr( sizeof( MSG_HEADER_TYPE ) )
|
||||
{
|
||||
m_cubStruct = cubStruct;
|
||||
|
||||
// Alloc a buffer
|
||||
m_cubPkt = m_cubMsgHdr + m_cubStruct;
|
||||
m_pubPkt = (uint8 *) g_MemPoolMsg.Alloc( m_cubPkt + cubReserve );
|
||||
m_pubBody = m_pubPkt + m_cubMsgHdr;
|
||||
memset(m_pubPkt, 0, m_cubPkt );
|
||||
m_bAlloced = true;
|
||||
m_pubVarRead = NULL;
|
||||
}
|
||||
|
||||
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
CMsgBase_t<MSG_HEADER_TYPE>::CMsgBase_t( const uint8 *pubPkt, uint32 cubPkt )
|
||||
: m_cubMsgHdr( 0 )
|
||||
{
|
||||
m_cubStruct = 0;
|
||||
|
||||
// Alloc a buffer
|
||||
m_cubPkt = cubPkt;
|
||||
m_pubPkt = (uint8 *) g_MemPoolMsg.Alloc( m_cubPkt );
|
||||
m_pubBody = m_pubPkt + m_cubMsgHdr;
|
||||
Q_memcpy(m_pubPkt, pubPkt, cubPkt );
|
||||
m_bAlloced = true;
|
||||
m_pubVarRead = NULL;
|
||||
}
|
||||
|
||||
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
CMsgBase_t<MSG_HEADER_TYPE>::CMsgBase_t( uint32 cubHdr, uint32 cubStruct, uint8 *pubPkt, uint32 cubPkt )
|
||||
: m_cubMsgHdr( cubHdr )
|
||||
{
|
||||
Assert( cubHdr != 0 );
|
||||
Assert( !cubPkt || ( cubPkt >= ( cubHdr + cubStruct ) ) );
|
||||
m_cubStruct = cubStruct;
|
||||
m_pubPkt = pubPkt;
|
||||
m_pubBody = m_pubPkt + m_cubMsgHdr;
|
||||
m_cubPkt = cubPkt;
|
||||
m_bAlloced = false;
|
||||
m_pubVarRead = PubVarData();
|
||||
}
|
||||
|
||||
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
CMsgBase_t<MSG_HEADER_TYPE>::~CMsgBase_t()
|
||||
{
|
||||
// if we allocated memory, free it
|
||||
if ( m_bAlloced && m_pubPkt )
|
||||
g_MemPoolMsg.Free( m_pubPkt );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: ensure the packet can contain at least this much extra data
|
||||
// Input: cubExtraSize - the amount of bytes to have room for
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::SetPacket( IMsgNetPacket *pNetPacket )
|
||||
{
|
||||
m_pubPkt = pNetPacket->PubData();
|
||||
m_pubBody = m_pubPkt + m_cubMsgHdr;
|
||||
m_cubPkt = pNetPacket->CubData();
|
||||
Assert( !m_cubPkt || ( m_cubPkt >= ( m_cubMsgHdr + m_cubStruct ) ) );
|
||||
m_bAlloced = false;
|
||||
m_pubVarRead = PubVarData();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: ensure the packet can contain at least this much extra data
|
||||
// Input: cubExtraSize - the amount of bytes to have room for
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::EnsurePacketSize( uint32 cubExtraSize )
|
||||
{
|
||||
m_pubPkt = (uint8 *) g_MemPoolMsg.ReAlloc( m_pubPkt, m_cubPkt + cubExtraSize );
|
||||
m_pubBody = m_pubPkt + m_cubMsgHdr;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends a bool to the variable length data associated with this message
|
||||
// Input: bData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddBoolData( bool ubData )
|
||||
{
|
||||
return AddUint8Data( static_cast< uint8 >( ubData ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends a uint8 to the variable length data associated with this message
|
||||
// Input: ubData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddUint8Data( uint8 ubData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( uint8 ) );
|
||||
*( ( uint8 * ) ( m_pubPkt + m_cubPkt ) ) = ubData;
|
||||
m_cubPkt += sizeof( uint8 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends a uint to the variable length data associated with this message
|
||||
// Input: unData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddUintData( uint32 unData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( uint32 ) );
|
||||
*( ( uint32 * ) ( m_pubPkt + m_cubPkt ) ) = unData;
|
||||
m_cubPkt += sizeof( uint32 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends an int to the variable length data associated with this message
|
||||
// Input: nData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddIntData( int32 nData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( int32 ) );
|
||||
*( ( int32 * ) ( m_pubPkt + m_cubPkt ) ) = nData;
|
||||
m_cubPkt += sizeof( int32 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends an int16 to the variable length data associated with this message
|
||||
// Input: nData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddInt16Data( int16 sData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( int16 ) );
|
||||
*( ( int16 * ) ( m_pubPkt + m_cubPkt ) ) = sData;
|
||||
m_cubPkt += sizeof( int16 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends an int16 to the variable length data associated with this message
|
||||
// Input: nData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddUint16Data( uint16 usData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( uint16 ) );
|
||||
*( ( uint16 * ) ( m_pubPkt + m_cubPkt ) ) = usData;
|
||||
m_cubPkt += sizeof( uint16 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends a uint64 to the variable length data associated with this message
|
||||
// Input: ulData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddUint64Data( uint64 ulData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( uint64 ) );
|
||||
*( ( uint64 * ) ( m_pubPkt + m_cubPkt ) ) = ulData;
|
||||
m_cubPkt += sizeof( uint64 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends an int64 to the variable length data associated with this message
|
||||
// Input: lData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddInt64Data( int64 lData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( int64 ) );
|
||||
*( ( int64 * ) ( m_pubPkt + m_cubPkt ) ) = lData;
|
||||
m_cubPkt += sizeof( int64 );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends variable length data to this message. (Can be called
|
||||
// repeatedly to append multiple data blocks.)
|
||||
// Input: pvData - pointer to data to append
|
||||
// cubData - size of data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddVariableLenData( const void *pvData, uint cubLen )
|
||||
{
|
||||
if ( cubLen > 0 )
|
||||
{
|
||||
EnsurePacketSize( cubLen );
|
||||
memcpy( ( m_pubPkt + m_cubPkt ), pvData, cubLen );
|
||||
m_cubPkt += cubLen;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends a float to the variable length data associated with this message
|
||||
// Input: nData - data to append
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddFloatData( float flData )
|
||||
{
|
||||
EnsurePacketSize( sizeof( float ) );
|
||||
*( ( float * ) ( m_pubPkt + m_cubPkt ) ) = flData;
|
||||
m_cubPkt += sizeof( float );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Appends a string to the variable-length portion of this message.
|
||||
// Input: pchIn - String to append to the message
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddStrData( const char *pchIn )
|
||||
{
|
||||
if ( !pchIn )
|
||||
{
|
||||
Assert( pchIn ); // passing a null string here is a code bug
|
||||
return;
|
||||
}
|
||||
|
||||
int cchIn = Q_strlen( pchIn );
|
||||
|
||||
EnsurePacketSize( cchIn + 1 );
|
||||
|
||||
Q_strncpy( ( char * ) ( m_pubPkt + m_cubPkt ), pchIn, cchIn + 1 );
|
||||
m_cubPkt += ( cchIn + 1 );
|
||||
}
|
||||
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
template <typename T>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::AddStructure( T& structure )
|
||||
{
|
||||
|
||||
EnsurePacketSize(sizeof(structure)) ;
|
||||
*( reinterpret_cast<T*>(m_pubPkt+m_cubPkt)) = structure ;
|
||||
m_cubPkt += sizeof(structure) ;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Read a bool from the variable-length part of the message
|
||||
// Input: pbData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadBoolData( bool *pbData )
|
||||
{
|
||||
return BReadUint8Data( ( uint8* ) pbData );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Read a uint8 from the variable-length part of the message
|
||||
// Input: pbData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadUint8Data( uint8 *pbData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( uint8 ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*pbData = * ( ( uint8 * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( uint8 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Read a uint32 from the variable-length part of the message
|
||||
// Input: punData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadUintData( uint32 *punData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( uint32 ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*punData = * ( ( uint32 * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( uint32 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads an int32 from the variable-length part of the message
|
||||
// Input: pnData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadIntData( int32 *pnData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( int32 ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*pnData = * ( ( int32 * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( int32 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads an int16 from the variable-length part of the message
|
||||
// Input: pnData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadInt16Data( int16 *psData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( int16 ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*psData = * ( ( int16 * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( int16 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads an uint16 from the variable-length part of the message
|
||||
// Input: pnData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadUint16Data( uint16 *pusData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( uint16 ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*pusData = * ( ( uint16 * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( uint16 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Read a uint64 from the variable-length part of the message
|
||||
// Input: pulData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadUint64Data( uint64 *pulData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( uint64 ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*pulData = * ( ( uint64 * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( uint64 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads an int64 from the variable-length part of the message
|
||||
// Input: plData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadInt64Data( int64 *plData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( int64 ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*plData = * ( ( int64 * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( int64 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads a float from the variable-length part of the message
|
||||
// Input: pflData - [return] The value we read goes here
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadFloatData( float *pflData )
|
||||
{
|
||||
if ( m_pubVarRead + sizeof( float ) > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
*pflData = * ( ( float * ) m_pubVarRead );
|
||||
m_pubVarRead += sizeof( float );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads a block of data from the variable-length part of the message
|
||||
// Input: pvBuff - [return] Buffer to copy the data into
|
||||
// cubRead - Amount of data to read
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadVariableLenData( void *pvBuff, uint32 cubRead )
|
||||
{
|
||||
if ( m_pubVarRead + cubRead > m_pubPkt + m_cubPkt )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_memcpy( pvBuff, m_pubVarRead, cubRead );
|
||||
m_pubVarRead += cubRead;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads a string from the variable-length part of the message
|
||||
// Input: pchBuff - [return] Buffer to copy the string into
|
||||
// cchBuff - Size of the buffer
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadStr( char *pchBuff, int cchBuff )
|
||||
{
|
||||
int cchRead = 0;
|
||||
int cchLeft = CubReadRemaining(); // get bytes left in message
|
||||
|
||||
// search for string end in rest of message
|
||||
while ( cchRead < cchLeft )
|
||||
{
|
||||
// if we hit the 0, stop
|
||||
if ( *(m_pubVarRead+cchRead) == 0 )
|
||||
break;
|
||||
|
||||
cchRead++;
|
||||
}
|
||||
|
||||
cchRead++; // add the 0
|
||||
|
||||
// check if string fits into buffer and was found within packet bounds
|
||||
if ( ( cchRead > cchBuff ) || (cchRead > cchLeft) )
|
||||
{
|
||||
// at least return an empty string since most code doesn't check the return value
|
||||
if ( cchBuff > 0 )
|
||||
pchBuff[0] = 0;
|
||||
|
||||
ReportBufferOverflow();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the string to output buffer
|
||||
Q_memcpy( pchBuff, m_pubVarRead, cchRead );
|
||||
m_pubVarRead += ( cchRead * sizeof( char ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads a string from the variable-length part of the message
|
||||
// Input: pchString - [return] copied string
|
||||
// Output: true if we were able to read, false if we overran the buffer
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadStr( CUtlString *pstr )
|
||||
{
|
||||
if ( pstr == NULL )
|
||||
return false;
|
||||
|
||||
int cchRead = 0;
|
||||
int cchLeft = CubReadRemaining(); // get bytes left in message
|
||||
|
||||
// search for string end in rest of message
|
||||
while ( cchRead < cchLeft )
|
||||
{
|
||||
// if we hit the 0, stop
|
||||
if ( *(m_pubVarRead+cchRead) == 0 )
|
||||
break;
|
||||
|
||||
cchRead++;
|
||||
}
|
||||
|
||||
cchRead++; // add the 0
|
||||
|
||||
// check if string was found within packet bounds
|
||||
if ( cchRead > cchLeft )
|
||||
{
|
||||
ReportBufferOverflow();
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the string
|
||||
*pstr = (const char*)m_pubVarRead;
|
||||
m_pubVarRead += ( cchRead * sizeof( char ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
template <typename T>
|
||||
bool CMsgBase_t<MSG_HEADER_TYPE>::BReadStructure( T& structure )
|
||||
{
|
||||
int cbLeft = CubReadRemaining() ;
|
||||
|
||||
if( cbLeft >= sizeof(structure))
|
||||
{
|
||||
structure = *( reinterpret_cast<T*>(m_pubVarRead)) ;
|
||||
m_pubVarRead += sizeof(structure) ;
|
||||
return true ;
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::PacketDump()
|
||||
{
|
||||
//if ( !g_ConVarMsgErrorDump.GetBool() )
|
||||
// return;
|
||||
|
||||
EmitInfo( SPEW_NETWORK, SPEW_NEVER, LOG_ALWAYS, "Packet dump: raw size %u, header size %u, body size %u, var size %u\n", m_cubPkt, m_cubMsgHdr, m_cubStruct, CubVarData() );
|
||||
|
||||
EmitInfo( SPEW_NETWORK, SPEW_NEVER, LOG_ALWAYS, "Header dump: %s\n", Hdr().GetHeaderDescription() );
|
||||
|
||||
EmitInfo( SPEW_NETWORK, SPEW_NEVER, LOG_ALWAYS, "Struct dump: %u bytes\n", m_cubStruct );
|
||||
char szLine[100] = "";
|
||||
char szText[32] = "";
|
||||
|
||||
for ( uint i=0; i<m_cubStruct; i++ )
|
||||
{
|
||||
byte nValue = PubBody()[i];
|
||||
uint nIndex = i%16;
|
||||
|
||||
Q_snprintf( szLine+3*nIndex, 8, "%02X ", nValue );
|
||||
|
||||
if ( nValue > 31 && nValue != '%' )
|
||||
szText[nIndex] = nValue;
|
||||
else
|
||||
szText[nIndex] = '.';
|
||||
|
||||
if ( nIndex == 15 || i==(m_cubStruct-1))
|
||||
{
|
||||
szText[nIndex+1] = '\n';
|
||||
szText[nIndex+2] = 0;
|
||||
Q_strcat( szLine, "; ", sizeof(szLine) );
|
||||
Q_strcat( szLine, szText, sizeof(szLine) );
|
||||
EmitInfo( SPEW_NETWORK, SPEW_NEVER, LOG_ALWAYS, "%s", szLine );
|
||||
szLine[0]=0;
|
||||
}
|
||||
}
|
||||
|
||||
uint cubVarData = MIN( CubVarData(), 1024u );
|
||||
|
||||
EmitInfo( SPEW_NETWORK, SPEW_NEVER, LOG_ALWAYS, "VarData dump: %u bytes\n", cubVarData );
|
||||
|
||||
for ( uint i=0; i<cubVarData; i++ )
|
||||
{
|
||||
byte nValue = PubVarData()[i];
|
||||
uint nIndex = i%16;
|
||||
|
||||
Q_snprintf( szLine+3*nIndex, 8, "%02X ", nValue );
|
||||
|
||||
if ( nValue > 31 && nValue != '%' )
|
||||
szText[nIndex] = nValue;
|
||||
else
|
||||
szText[nIndex] = '.';
|
||||
|
||||
if ( nIndex == 15 || i==(cubVarData-1))
|
||||
{
|
||||
szText[nIndex+1] = '\n';
|
||||
szText[nIndex+2] = 0;
|
||||
Q_strcat( szLine, " ; ", sizeof(szLine) );
|
||||
Q_strcat( szLine, szText, sizeof(szLine) );
|
||||
EmitInfo( SPEW_NETWORK, SPEW_NEVER, LOG_ALWAYS, "%s", szLine );
|
||||
szLine[0]=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MSG_HEADER_TYPE>
|
||||
void CMsgBase_t<MSG_HEADER_TYPE>::ReportBufferOverflow()
|
||||
{
|
||||
EmitWarning( SPEW_NETWORK, SPEW_ALWAYS, "Read buffer overflowed on incoming %s packet\n", Hdr().PchMsgName( ) );
|
||||
PacketDump();
|
||||
}
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCMSGBASE_H
|
||||
470
public/gcsdk/msgprotobuf.h
Normal file
470
public/gcsdk/msgprotobuf.h
Normal file
@@ -0,0 +1,470 @@
|
||||
//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef MSGPROTOBUF_H
|
||||
#define MSGPROTOBUF_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "msgbase.h"
|
||||
#include "gcmsg.h"
|
||||
#include "tier0/tslist.h"
|
||||
|
||||
// eliminates a conflict with TYPE_BOOL in OSX
|
||||
#ifdef TYPE_BOOL
|
||||
#undef TYPE_BOOL
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning( disable:4512 )
|
||||
#include <tier0/valve_minmax_off.h>
|
||||
#include "steammessages.pb.h"
|
||||
#include <tier0/valve_minmax_on.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CProtoBufNetPacket
|
||||
// Thin wrapper around raw CNetPacket which implements our IMsgNetPacket interface.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CProtoBufNetPacket : public IMsgNetPacket
|
||||
{
|
||||
#ifdef GC
|
||||
DECLARE_CLASS_MEMPOOL( CProtoBufNetPacket );
|
||||
#endif
|
||||
|
||||
public:
|
||||
CProtoBufNetPacket( CNetPacket *pNetPacket, GCProtoBufMsgSrc eReplyType, const CSteamID steamID, uint32 nGCDirIndex, MsgType_t msgType );
|
||||
|
||||
EMsgFormatType GetEMsgFormatType() const { return k_EMsgFormatTypeProtocolBuffer; }
|
||||
CNetPacket *GetCNetPacket() const { return m_pNetPacket; }
|
||||
uint8 *PubData() const { return m_pNetPacket->PubData(); }
|
||||
uint CubData() const { return m_pNetPacket->CubData(); }
|
||||
|
||||
bool IsValid() const { return m_bIsValid; }
|
||||
ProtoBufMsgHeader_t &GetFixedHeader() const { return *( (ProtoBufMsgHeader_t *)PubData() ); }
|
||||
CMsgProtoBufHeader *GetProtoHeader() const { return m_pHeader; }
|
||||
|
||||
MsgType_t GetEMsg() const { return m_msgType; }
|
||||
JobID_t GetSourceJobID() const { return m_pHeader->job_id_source(); }
|
||||
JobID_t GetTargetJobID() const { return m_pHeader->job_id_target(); }
|
||||
void SetTargetJobID( JobID_t ulJobID ) { m_pHeader->set_job_id_target( ulJobID ); }
|
||||
|
||||
CSteamID GetSteamID() const { return m_steamID; }
|
||||
void SetSteamID( CSteamID steamID ) { m_steamID = steamID; }
|
||||
|
||||
AppId_t GetSourceAppID() const { return m_pHeader->source_app_id(); };
|
||||
void SetSourceAppID( AppId_t appId ) { m_pHeader->set_source_app_id( appId ); }
|
||||
|
||||
virtual bool BHasTargetJobName() const { return m_pHeader->has_target_job_name(); }
|
||||
virtual const char *GetTargetJobName() const { return m_pHeader->target_job_name().c_str(); }
|
||||
|
||||
//called to obtain access to the body portion of the net packet associated with this message. Will return NULL if not in a valid state
|
||||
bool GetMsgBody( const uint8*& pubData, uint32& cubData ) const;
|
||||
|
||||
private:
|
||||
virtual ~CProtoBufNetPacket();
|
||||
|
||||
CNetPacket *m_pNetPacket;
|
||||
CMsgProtoBufHeader *m_pHeader;
|
||||
CSteamID m_steamID;
|
||||
MsgType_t m_msgType;
|
||||
bool m_bIsValid;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CProtoBufMsgBase - Base class for templated protobuf msgs. As much code is
|
||||
// in this class as possible to reduce template copy overhead
|
||||
//-----------------------------------------------------------------------------
|
||||
class CProtoBufMsgBase
|
||||
{
|
||||
public:
|
||||
// allows any kind of destination to be the target of an AsyncSend
|
||||
class IProtoBufSendHandler
|
||||
{
|
||||
public:
|
||||
virtual bool BAsyncSend( MsgType_t eMsg, const uint8 *pubMsgBytes, uint32 cubSize ) = 0;
|
||||
};
|
||||
|
||||
// Receive constructor. We expect InitFromPacket will be called later
|
||||
CProtoBufMsgBase();
|
||||
// Send constructor. InitFromPacket must not be called later
|
||||
CProtoBufMsgBase( MsgType_t eMsgType );
|
||||
|
||||
virtual ~CProtoBufMsgBase();
|
||||
|
||||
bool InitFromPacket( IMsgNetPacket * pNetPacket );
|
||||
bool BAsyncSend( IProtoBufSendHandler & pSender ) const;
|
||||
bool BAsyncSendWithPreSerializedBody( IProtoBufSendHandler & pSender, const byte *pubBody, uint32 cubBody ) const;
|
||||
//free standing version to send a protobuff given a header and pre-serialized body. Primarily used for efficient message routing
|
||||
static bool BAsyncSendWithPreSerializedBody( IProtoBufSendHandler& sender, MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, const byte* pubBody, uint32 cubBody );
|
||||
//similar to the above, but sends a protobuf object that will be serialized into the buffer
|
||||
static bool BAsyncSendProto( IProtoBufSendHandler& sender, MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, const ::google::protobuf::Message& proto );
|
||||
|
||||
CMsgProtoBufHeader &Hdr() { return *m_pProtoBufHdr; }
|
||||
const CMsgProtoBufHeader &Hdr() const { return *m_pProtoBufHdr; }
|
||||
const CMsgProtoBufHeader &ConstHdr() const { return *m_pProtoBufHdr; }
|
||||
|
||||
MsgType_t GetEMsg() const { return m_eMsg & (~k_EMsgProtoBufFlag); }
|
||||
CSteamID GetClientSteamID() const { return CSteamID( static_cast<uint64>( m_pProtoBufHdr->client_steam_id() ) ); }
|
||||
JobID_t GetJobIDTarget() const { return m_pProtoBufHdr->job_id_target(); }
|
||||
JobID_t GetJobIDSource() const { return m_pProtoBufHdr->job_id_source(); }
|
||||
AppId_t GetSourceAppID() const { return m_pProtoBufHdr->source_app_id(); }
|
||||
bool BIsExpectingReply() const { return GetJobIDSource() != k_GIDNil; }
|
||||
|
||||
void SetJobIDSource( JobID_t jobId ) { m_pProtoBufHdr->set_job_id_source( jobId ); }
|
||||
void SetJobIDTarget( JobID_t jobId ) { m_pProtoBufHdr->set_job_id_target( jobId ); }
|
||||
void SetSourceAppID( AppId_t appId ) { m_pProtoBufHdr->set_source_app_id( appId ); }
|
||||
void ExpectingReply( JobID_t jobId ) { SetJobIDSource( jobId ); }
|
||||
|
||||
EResult GetEResult() const { return (EResult)ConstHdr().eresult(); }
|
||||
void SetEResult( EResult eResult ) { Hdr().set_eresult( eResult ); }
|
||||
const char *GetErrorMessage() const { return ConstHdr().error_message().c_str(); }
|
||||
void SetErrorMessage( const char *pchErrorMessage ) { Hdr().set_error_message( pchErrorMessage ); }
|
||||
void AppendErrorMessage( const char *pchErrorMessage ) { Hdr().mutable_error_message()->append( pchErrorMessage ); }
|
||||
|
||||
// Must be implemented by subclasses. Returns the body. The templated subclasses have their
|
||||
// own body accessor that returns the body as the specific type
|
||||
virtual ::google::protobuf::Message *GetGenericBody() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
// Mutex to use when registering a new pool type
|
||||
static CThreadMutex s_PoolRegMutex;
|
||||
|
||||
private:
|
||||
|
||||
//utility function that handles allocating a memory pool big enough for the provided header and specified body
|
||||
//size and writing the header into the pool. This will return a pointer to the memory, as well as the header size.
|
||||
static uint8* AllocateMessageMemory( MsgType_t eMsgType, const CMsgProtoBufHeader& hdr, uint32 cubBodySize, uint32* pCubTotalSizeOut );
|
||||
//called to free the memory returned by allocate message memory
|
||||
static void FreeMessageMemory( uint8* pMemory );
|
||||
|
||||
// Pointer to an external net packet if we have one. If we have one then we will
|
||||
// not have allocated m_pProtoBufHdr ourselves
|
||||
CProtoBufNetPacket *m_pNetPacket;
|
||||
|
||||
// Protobuf objects for extended pb based header
|
||||
CMsgProtoBufHeader *m_pProtoBufHdr;
|
||||
|
||||
// Our message type
|
||||
MsgType_t m_eMsg;
|
||||
|
||||
// Private and unimplemented. Implement these if you want to be able to copy
|
||||
// these objects.
|
||||
CProtoBufMsgBase( const CProtoBufMsgBase& );
|
||||
CProtoBufMsgBase& operator=( const CProtoBufMsgBase& );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CProtoBufMsgMemoryPoolBase - Interface to allocation pools for each protobufmsg type
|
||||
//-----------------------------------------------------------------------------
|
||||
class CProtoBufMsgMemoryPoolBase
|
||||
{
|
||||
public:
|
||||
CProtoBufMsgMemoryPoolBase( uint32 unTargetLow, uint32 unTargetHigh );
|
||||
virtual ~CProtoBufMsgMemoryPoolBase();
|
||||
|
||||
// Memory interface
|
||||
::google::protobuf::Message *Alloc();
|
||||
void Free( ::google::protobuf::Message *pMsg );
|
||||
|
||||
// Stats
|
||||
uint32 GetEstimatedSize();
|
||||
uint32 GetAllocated() { return m_unAllocated; }
|
||||
uint32 GetFree() { return m_pTSQueueFreeObjects->Count(); }
|
||||
uint32 GetAllocHitCount() { return m_unAllocHitCounter; }
|
||||
uint32 GetAllocMissCount() { return m_unAllocMissCounter; }
|
||||
|
||||
// To be overriden by the templated class
|
||||
virtual CUtlString GetName() = 0;
|
||||
|
||||
protected:
|
||||
// The actual memory management. Must be overriden by the templated class
|
||||
virtual google::protobuf::Message *InternalAlloc() = 0;
|
||||
virtual void InternalFree( google::protobuf::Message *pMsg ) = 0;
|
||||
|
||||
// Called by the derived destructor to deallocate the outstanding messages
|
||||
bool PopItem( google::protobuf::Message **ppMsg );
|
||||
|
||||
private:
|
||||
CTSQueue<google::protobuf::Message *> *m_pTSQueueFreeObjects;
|
||||
|
||||
// These counters are important to get correct, so interlocked in case of allocating on threads
|
||||
CInterlockedInt m_unAllocHitCounter;
|
||||
CInterlockedInt m_unAllocMissCounter;
|
||||
CInterlockedInt m_unAllocated;
|
||||
|
||||
// Only set at construction, so not needed to be thread safe
|
||||
uint32 m_unTargetCountLow;
|
||||
uint32 m_unTargetCountHigh;
|
||||
};
|
||||
|
||||
} // namespace GCDSK
|
||||
|
||||
// The rest of the file needs memdbgon because the code in the templates do actual allocation
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CProtoBufMsgMemoryPool - Implementation for allocation pools for protobufmsgs.
|
||||
// We create one of these per protobuf msg type, created on first construction of
|
||||
// an object of that type.
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename PB_OBJECT_TYPE >
|
||||
class CProtoBufMsgMemoryPool : public CProtoBufMsgMemoryPoolBase
|
||||
{
|
||||
public:
|
||||
CProtoBufMsgMemoryPool()
|
||||
: CProtoBufMsgMemoryPoolBase( PB_OBJECT_TYPE::descriptor()->options().GetExtension( msgpool_soft_limit ),
|
||||
PB_OBJECT_TYPE::descriptor()->options().GetExtension( msgpool_hard_limit ) ) {}
|
||||
virtual ~CProtoBufMsgMemoryPool()
|
||||
{
|
||||
google::protobuf::Message *pObject = NULL;
|
||||
while ( PopItem( &pObject ) )
|
||||
{
|
||||
InternalFree( pObject );
|
||||
}
|
||||
}
|
||||
|
||||
virtual CUtlString GetName() OVERRIDE
|
||||
{
|
||||
return PB_OBJECT_TYPE::default_instance().GetTypeName().c_str();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ::google::protobuf::Message *InternalAlloc()
|
||||
{
|
||||
PB_OBJECT_TYPE *pObject = (PB_OBJECT_TYPE *)malloc( sizeof( PB_OBJECT_TYPE ) );
|
||||
Construct( pObject );
|
||||
return pObject;
|
||||
}
|
||||
|
||||
virtual void InternalFree( google::protobuf::Message *pMsg )
|
||||
{
|
||||
if ( NULL == pMsg )
|
||||
{
|
||||
Assert( NULL != pMsg );
|
||||
return;
|
||||
}
|
||||
|
||||
PB_OBJECT_TYPE *pObject = (PB_OBJECT_TYPE *)pMsg;
|
||||
Destruct( pObject );
|
||||
free( pObject );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CProtoBufMsgMemoryPoolMgr - Manages all the message pools for protobufmsgs.
|
||||
// Should have one global singleton instance of this which tracks all the pools
|
||||
// for individual message types.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CProtoBufMsgMemoryPoolMgr
|
||||
{
|
||||
public:
|
||||
CProtoBufMsgMemoryPoolMgr();
|
||||
~CProtoBufMsgMemoryPoolMgr();
|
||||
|
||||
void RegisterPool( CProtoBufMsgMemoryPoolBase *pPool );
|
||||
void DumpPoolInfo();
|
||||
|
||||
CMsgProtoBufHeader *AllocProtoBufHdr() { return (CMsgProtoBufHeader *)m_PoolHeaders.Alloc(); }
|
||||
void FreeProtoBufHdr( CMsgProtoBufHeader *pObject ) { m_PoolHeaders.Free( pObject ); }
|
||||
|
||||
private:
|
||||
CProtoBufMsgMemoryPool<CMsgProtoBufHeader> m_PoolHeaders;
|
||||
CUtlVector< CProtoBufMsgMemoryPoolBase * > m_vecMsgPools;
|
||||
};
|
||||
|
||||
extern CProtoBufMsgMemoryPoolMgr *GProtoBufMsgMemoryPoolMgr();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CProtoBufPtrMsg
|
||||
// Similar to a CProtoBufMsg, but the constructor simply takes in a pointer which is a
|
||||
// pointer to the protobuf object that is being wrapped by a message. This memory is managed
|
||||
// by the caller, this object does nothing to free the memory
|
||||
//-----------------------------------------------------------------------------
|
||||
class CProtoBufPtrMsg : public CProtoBufMsgBase
|
||||
{
|
||||
public:
|
||||
CProtoBufPtrMsg( google::protobuf::Message *pProto ) : m_pProtoBufBody( pProto ) {}
|
||||
|
||||
private:
|
||||
virtual google::protobuf::Message *GetGenericBody() const OVERRIDE { return m_pProtoBufBody; }
|
||||
|
||||
// Protobuf object for the message body
|
||||
google::protobuf::Message *m_pProtoBufBody;
|
||||
|
||||
// Private and unimplemented. Implement these if you want to be able to copy
|
||||
// these objects.
|
||||
CProtoBufPtrMsg( const CProtoBufPtrMsg& );
|
||||
CProtoBufPtrMsg& operator=( const CProtoBufPtrMsg& );
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CProtoBufMsg
|
||||
// New style steam inter-server message class based on Google Protocol Buffers
|
||||
// Handles a message with a header of type MsgHdr_t, a body of type T, and optional variable length data
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename PB_OBJECT_TYPE >
|
||||
class CProtoBufMsg : public CProtoBufMsgBase
|
||||
{
|
||||
private:
|
||||
static bool s_bRegisteredWithMemoryPoolMgr;
|
||||
static CProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *s_pMemoryPool;
|
||||
|
||||
public:
|
||||
|
||||
// Used to alloc a protobuf of this type from the pool. Can be used by functions
|
||||
// working with protobufs that aren't messages to take advantage of pooling
|
||||
static PB_OBJECT_TYPE *AllocProto()
|
||||
{
|
||||
// If we haven't done registration do so now
|
||||
// Called on construction of each object of this type, but only does work
|
||||
// once to setup memory pools for the class type.
|
||||
if ( !s_bRegisteredWithMemoryPoolMgr )
|
||||
{
|
||||
// Get the lock and make sure we still haven't
|
||||
s_PoolRegMutex.Lock();
|
||||
if ( !s_bRegisteredWithMemoryPoolMgr )
|
||||
{
|
||||
s_pMemoryPool = new CProtoBufMsgMemoryPool< PB_OBJECT_TYPE >();
|
||||
GProtoBufMsgMemoryPoolMgr()->RegisterPool( s_pMemoryPool );
|
||||
s_bRegisteredWithMemoryPoolMgr = true;
|
||||
}
|
||||
s_PoolRegMutex.Unlock();
|
||||
}
|
||||
|
||||
return static_cast<PB_OBJECT_TYPE *>( s_pMemoryPool->Alloc() );
|
||||
}
|
||||
|
||||
// Frees a protobuf allocated with AllocProto()
|
||||
static void FreeProto( PB_OBJECT_TYPE *pbObj )
|
||||
{
|
||||
s_pMemoryPool->Free( pbObj );
|
||||
}
|
||||
|
||||
|
||||
// Constructor for an empty message
|
||||
CProtoBufMsg( MsgType_t eMsg )
|
||||
: CProtoBufMsgBase( eMsg )
|
||||
, m_pProtoBufBody( NULL )
|
||||
{
|
||||
VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( MsgType_t )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
||||
m_pProtoBufBody = AllocProto();
|
||||
}
|
||||
|
||||
// Constructor for an empty message responding to a client
|
||||
CProtoBufMsg( MsgType_t eMsg, CSteamID steamIDClient, int32 nSessionIDClient )
|
||||
: CProtoBufMsgBase( eMsg )
|
||||
, m_pProtoBufBody( NULL )
|
||||
{
|
||||
VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( MsgType_t, CSteamID, int32 )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
||||
|
||||
m_pProtoBufBody = AllocProto();
|
||||
Hdr()->set_client_steam_id( steamIDClient.ConvertToUint64() );
|
||||
Hdr()->set_client_session_id( nSessionIDClient );
|
||||
}
|
||||
|
||||
// Constructor from an incoming netpacket
|
||||
CProtoBufMsg( IMsgNetPacket *pNetPacket )
|
||||
: CProtoBufMsgBase()
|
||||
, m_pProtoBufBody( NULL )
|
||||
{
|
||||
m_pProtoBufBody = AllocProto();
|
||||
InitFromPacket( pNetPacket );
|
||||
}
|
||||
|
||||
// constructor for use in catching replies or any other place where you have nothing to stuff in
|
||||
// the message at construct time
|
||||
CProtoBufMsg()
|
||||
: CProtoBufMsgBase()
|
||||
, m_pProtoBufBody( NULL )
|
||||
{
|
||||
m_pProtoBufBody = AllocProto();
|
||||
}
|
||||
|
||||
// Constructor for replying to another protobuf message
|
||||
CProtoBufMsg( MsgType_t eMsg, const CProtoBufMsgBase & msgReplyingTo )
|
||||
: CProtoBufMsgBase( eMsg )
|
||||
, m_pProtoBufBody( NULL )
|
||||
{
|
||||
VPROF_BUDGET( "CProtoBufMsg::CProtoBufMsg( EMsg, CProtoBufMsgMemoryPoolBase )", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
||||
m_pProtoBufBody = AllocProto();
|
||||
|
||||
// set up the actual reply
|
||||
SetJobIDTarget( msgReplyingTo.GetJobIDSource() );
|
||||
}
|
||||
|
||||
// Destructor
|
||||
virtual ~CProtoBufMsg()
|
||||
{
|
||||
if ( m_pProtoBufBody )
|
||||
{
|
||||
FreeProto( m_pProtoBufBody );
|
||||
m_pProtoBufBody = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
PB_OBJECT_TYPE &Body() { return *m_pProtoBufBody; }
|
||||
const PB_OBJECT_TYPE &Body() const { return *m_pProtoBufBody; }
|
||||
|
||||
private:
|
||||
virtual google::protobuf::Message *GetGenericBody() const OVERRIDE { return m_pProtoBufBody; }
|
||||
|
||||
// Protobuf object for the message body
|
||||
PB_OBJECT_TYPE *m_pProtoBufBody;
|
||||
|
||||
// Private and unimplemented. Implement these if you want to be able to copy
|
||||
// these objects.
|
||||
CProtoBufMsg( const CProtoBufMsg& );
|
||||
CProtoBufMsg& operator=( const CProtoBufMsg& );
|
||||
};
|
||||
|
||||
// Statics
|
||||
template< typename PB_OBJECT_TYPE > bool CProtoBufMsg< PB_OBJECT_TYPE>::s_bRegisteredWithMemoryPoolMgr = false;
|
||||
template< typename PB_OBJECT_TYPE > CProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *CProtoBufMsg< PB_OBJECT_TYPE>::s_pMemoryPool = NULL;
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Wrapper class to handle alloc/free using the pool allocators
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class TMsg>
|
||||
class CProtoBufPoolObj
|
||||
{
|
||||
private:
|
||||
TMsg *m_pMsg;
|
||||
|
||||
private: // Disallow copying/assignment
|
||||
CProtoBufPoolObj( CProtoBufPoolObj const &x );
|
||||
CProtoBufPoolObj & operator = ( CProtoBufPoolObj const &x );
|
||||
|
||||
public:
|
||||
CProtoBufPoolObj() { m_pMsg = CProtoBufMsg<TMsg>::AllocProto(); }
|
||||
~CProtoBufPoolObj() { CProtoBufMsg<TMsg>::FreeProto( m_pMsg ); }
|
||||
|
||||
operator TMsg & () { return *m_pMsg; }
|
||||
};
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
// memdbgon is only supposed to be on in cpp files, turn it off in case the next thing includes has a conflict with it
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // MSGPROTOBUF_H
|
||||
48
public/gcsdk/netpacket.h
Normal file
48
public/gcsdk/netpacket.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Holds the CNetPacket class
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCNETPACKET_H
|
||||
#define GCNETPACKET_H
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: reference-counted received network packet
|
||||
//-----------------------------------------------------------------------------
|
||||
class CNetPacket
|
||||
{
|
||||
public:
|
||||
CNetPacket();
|
||||
~CNetPacket();
|
||||
|
||||
//called to allocate a buffer for the net packet of the specified size. This takes an optional pointer
|
||||
//of which it will copy into the data if appropriate
|
||||
void Init( uint32 cubData, const void* pCopyData = NULL );
|
||||
|
||||
//called when working with a net packet that you want to reference a separate buffer
|
||||
void InitAdoptBuffer( uint32 cubData, uint8* pubData );
|
||||
void OrphanBuffer();
|
||||
|
||||
// data
|
||||
uint8 *PubData() const { return m_pubData; }
|
||||
uint CubData() const { return m_cubData; }
|
||||
|
||||
// ownership
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
private:
|
||||
int m_cRef; // reference count, deletes self when 0
|
||||
uint m_cubData;
|
||||
uint8 *m_pubData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCNETPACKET_H
|
||||
42
public/gcsdk/netpacketpool.h
Normal file
42
public/gcsdk/netpacketpool.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCNETPACKETPOOL_H
|
||||
#define GCNETPACKETPOOL_H
|
||||
|
||||
#include "netpacket.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CNetPacket;
|
||||
|
||||
extern int g_cNetPacket;
|
||||
extern CThreadSafeMultiMemoryPool g_MemPoolMsg;
|
||||
|
||||
|
||||
class CNetPacketPool
|
||||
{
|
||||
public:
|
||||
|
||||
#ifdef _SERVER
|
||||
static int CMBPacketMemPool() { return ( CNetPacketPool::sm_MemPoolNetPacket.CubTotalSize() / k_nMegabyte ); }
|
||||
static int CMBPacketMemPoolInUse() { return ( CNetPacketPool::sm_MemPoolNetPacket.CubSizeInUse() / k_nMegabyte ); }
|
||||
#endif // _SERVER
|
||||
|
||||
static CNetPacket *AllocNetPacket() { g_cNetPacket++; return sm_MemPoolNetPacket.Alloc(); }
|
||||
|
||||
private:
|
||||
friend class CNetPacket;
|
||||
|
||||
static CClassMemoryPool<CNetPacket> sm_MemPoolNetPacket;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GCNETPACKETPOOL_H
|
||||
138
public/gcsdk/protobufsharedobject.h
Normal file
138
public/gcsdk/protobufsharedobject.h
Normal file
@@ -0,0 +1,138 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Shared object based on a CBaseRecord subclass
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef PROTOBUFSHAREDOBJECT_H
|
||||
#define PROTOBUFSHAREDOBJECT_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "google/protobuf/descriptor.h"
|
||||
#include "tier1/keyvalues.h"
|
||||
|
||||
#if defined( GC ) && defined( DEBUG )
|
||||
#include "gcbase.h"
|
||||
#endif
|
||||
|
||||
namespace google
|
||||
{
|
||||
namespace protobuf
|
||||
{
|
||||
class Message;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Base class for CProtoBufSharedObject. This is where all the actual
|
||||
// code lives.
|
||||
//----------------------------------------------------------------------------
|
||||
class CProtoBufSharedObjectBase : public CSharedObject
|
||||
{
|
||||
public:
|
||||
typedef CSharedObject BaseClass;
|
||||
|
||||
virtual bool BParseFromMessage( const CUtlBuffer & buffer ) OVERRIDE;
|
||||
virtual bool BParseFromMessage( const std::string &buffer ) OVERRIDE;
|
||||
virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) OVERRIDE;
|
||||
|
||||
virtual bool BAddToMessage( std::string *pBuffer ) const OVERRIDE;
|
||||
virtual bool BAddDestroyToMessage( std::string *pBuffer ) const OVERRIDE;
|
||||
|
||||
virtual bool BIsKeyLess( const CSharedObject & soRHS ) const ;
|
||||
virtual void Copy( const CSharedObject & soRHS );
|
||||
virtual void Dump() const OVERRIDE;
|
||||
|
||||
|
||||
#ifdef GC
|
||||
|
||||
virtual bool BParseFromMemcached( CUtlBuffer & buffer ) OVERRIDE;
|
||||
virtual bool BAddToMemcached( CUtlBuffer & bufOutput ) const OVERRIDE;
|
||||
#endif //GC
|
||||
|
||||
// Static helpers
|
||||
static bool SerializeToBuffer( const ::google::protobuf::Message & msg, CUtlBuffer & bufOutput );
|
||||
static void Dump( const ::google::protobuf::Message & msg );
|
||||
|
||||
protected:
|
||||
virtual ::google::protobuf::Message *GetPObject() = 0;
|
||||
const ::google::protobuf::Message *GetPObject() const { return const_cast<CProtoBufSharedObjectBase *>(this)->GetPObject(); }
|
||||
|
||||
private:
|
||||
static ::google::protobuf::Message *BuildDestroyToMessage( const ::google::protobuf::Message & msg );
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Template for making a shared object that uses a specific protobuf
|
||||
// message class for its wire protocol and in-memory representation.
|
||||
//----------------------------------------------------------------------------
|
||||
template< typename Message_t, int nTypeID >
|
||||
class CProtoBufSharedObject : public CProtoBufSharedObjectBase
|
||||
{
|
||||
public:
|
||||
~CProtoBufSharedObject()
|
||||
{
|
||||
#if defined( GC ) && defined( DEBUG )
|
||||
// Ensure this SO is not in any cache, or we have an error. We must provide the type since it is a virutal function otherwise
|
||||
Assert( !GGCBase()->IsSOCached( this, nTypeID ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual int GetTypeID() const { return nTypeID; }
|
||||
|
||||
Message_t & Obj() { return m_msgObject; }
|
||||
const Message_t & Obj() const { return m_msgObject; }
|
||||
|
||||
typedef Message_t SchObjectType_t;
|
||||
public:
|
||||
const static int k_nTypeID = nTypeID;
|
||||
|
||||
protected:
|
||||
::google::protobuf::Message *GetPObject() { return &m_msgObject; }
|
||||
|
||||
private:
|
||||
Message_t m_msgObject;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Special protobuf shared object that caches its serialized form
|
||||
//----------------------------------------------------------------------------
|
||||
template< typename Message_t, int nTypeID >
|
||||
class CProtoBufCachedSharedObject : public CProtoBufSharedObject< Message_t, nTypeID >
|
||||
{
|
||||
#ifdef GC
|
||||
public:
|
||||
virtual bool BAddToMessage( std::string *pBuffer ) const OVERRIDE
|
||||
{
|
||||
UpdateCache();
|
||||
*pBuffer = m_cachedSerialize;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClearCache() // You must call this when your object changes or your object won't update on the client!
|
||||
{
|
||||
m_cachedSerialize.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateCache() const
|
||||
{
|
||||
if ( m_cachedSerialize.empty() )
|
||||
{
|
||||
Obj().SerializeToString( &m_cachedSerialize );
|
||||
}
|
||||
}
|
||||
|
||||
mutable std::string m_cachedSerialize;
|
||||
#endif //GC
|
||||
};
|
||||
|
||||
} // GCSDK namespace
|
||||
|
||||
#endif //PROTOBUFSHAREDOBJECT_H
|
||||
107
public/gcsdk/refcount.h
Normal file
107
public/gcsdk/refcount.h
Normal file
@@ -0,0 +1,107 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCREFCOUNT_H
|
||||
#define GCREFCOUNT_H
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
// Base class for ref counted classes. Derive from this to be refcounted. Note:
|
||||
// you can no longer be deleted directly or declared on the stack. Make your
|
||||
// derived class' destructor private to ensure you can't be deleted directly or declared
|
||||
// on the stack.
|
||||
|
||||
// utility class
|
||||
template< class T >
|
||||
class CAutoPtr
|
||||
{
|
||||
T *m_pT;
|
||||
public:
|
||||
CAutoPtr()
|
||||
{
|
||||
m_pT = NULL;
|
||||
}
|
||||
|
||||
~CAutoPtr()
|
||||
{
|
||||
delete m_pT;
|
||||
}
|
||||
|
||||
T *reset( T *p )
|
||||
{
|
||||
delete m_pT;
|
||||
m_pT = p;
|
||||
return m_pT;
|
||||
}
|
||||
|
||||
T *TakeOwnership()
|
||||
{
|
||||
T *p = m_pT;
|
||||
m_pT = NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
T *release( )
|
||||
{
|
||||
T *pT = m_pT;
|
||||
m_pT = NULL;
|
||||
return pT;
|
||||
}
|
||||
|
||||
T *operator->()
|
||||
{
|
||||
return m_pT;
|
||||
}
|
||||
|
||||
operator T*()
|
||||
{
|
||||
return m_pT;
|
||||
}
|
||||
|
||||
protected:
|
||||
T *operator=(T*p)
|
||||
{
|
||||
AssertMsg( NULL == m_pT, "If this assert fires, you're leaking.\n" );
|
||||
m_pT = p;
|
||||
return m_pT;
|
||||
}
|
||||
};
|
||||
|
||||
class CRefCount
|
||||
{
|
||||
public:
|
||||
CRefCount() { m_cRef = 1; } // we are born with a ref count of 1
|
||||
|
||||
// increment ref count
|
||||
int AddRef() { return ThreadInterlockedIncrement( &m_cRef ); }
|
||||
|
||||
// delete ourselves when ref count reaches 0
|
||||
int Release()
|
||||
{
|
||||
Assert( m_cRef > 0 );
|
||||
int cRef = ThreadInterlockedDecrement( &m_cRef );
|
||||
if ( 0 == cRef )
|
||||
DestroyThis();
|
||||
return cRef;
|
||||
}
|
||||
protected:
|
||||
// Classes that derive from this should make their destructors private and virtual!
|
||||
virtual ~CRefCount() { Assert( 0 == m_cRef ); }
|
||||
|
||||
virtual void DestroyThis() { delete this; } // derived classes may override this if they want to be part of a mem pool
|
||||
|
||||
volatile int32 m_cRef; // ref count of this object
|
||||
};
|
||||
|
||||
#define SAFE_RELEASE( x ) if ( NULL != ( x ) ) { ( x )->Release(); x = NULL; }
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // GCREFCOUNT_H
|
||||
130
public/gcsdk/schemasharedobject.h
Normal file
130
public/gcsdk/schemasharedobject.h
Normal file
@@ -0,0 +1,130 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Shared object based on a CBaseRecord subclass
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef SCHEMASHAREDOBJECT_H
|
||||
#define SCHEMASHAREDOBJECT_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifndef GC
|
||||
#error "CSchemaSharedObject is only intended for use on GC-only shared objects. It shouldn't be included on the client"
|
||||
#endif
|
||||
|
||||
#if defined( DEBUG )
|
||||
#include "gcbase.h"
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Contains helper functions to deal with passed in CRecordInfo,
|
||||
// so that a CRecordInfo can be built on the fly
|
||||
//----------------------------------------------------------------------------
|
||||
class CSchemaSharedObjectHelper
|
||||
{
|
||||
public:
|
||||
static bool BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess, CRecordBase *pRecordBase );
|
||||
static bool BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, CRecordBase *pRecordBase, const CColumnSet & csDatabaseDirty );
|
||||
static bool BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess, CRecordBase *pRecordBase );
|
||||
static bool BYieldingAddToDatabase( CRecordBase *pRecordBase );
|
||||
static bool BYieldingReadFromDatabase( CRecordBase *pRecordBase );
|
||||
static void Dump( const CRecordBase *pRecordBase );
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Base class for CSchemaSharedObject. This is where all the actual
|
||||
// code lives.
|
||||
//----------------------------------------------------------------------------
|
||||
class CSchemaSharedObjectBase : public CSharedObject
|
||||
{
|
||||
public:
|
||||
typedef CSharedObject BaseClass;
|
||||
|
||||
CSchemaSharedObjectBase( CRecordInfo *pRecordInfo ) : BaseClass() {}
|
||||
|
||||
virtual bool BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess );
|
||||
virtual bool BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, const CUtlVector< int > &fields );
|
||||
virtual bool BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess );
|
||||
virtual bool BYieldingAddToDatabase();
|
||||
virtual bool BYieldingReadFromDatabase() ;
|
||||
|
||||
// schema shared objects are GC-only and are never sent to clients.
|
||||
virtual bool BIsNetworked() const { return false; }
|
||||
virtual bool BAddCreateToMessage( CUtlBuffer & bufCreate ) const { return false; }
|
||||
virtual bool BAddFullCreateToMessage( CUtlBuffer & bufCreate ) const { return false; }
|
||||
virtual bool BAddUpdateToMessage( CUtlBuffer & bufUpdate, const CUtlVector< int > &fields ) const { return false; }
|
||||
virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const { return false; }
|
||||
virtual bool BParseCreateFromMessage( const CUtlBuffer & bufCreate ) { return false; }
|
||||
virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate, const CUtlBuffer & bufUpdate ) { return false; }
|
||||
virtual bool BIsKeyLess( const CSharedObject & soRHS ) const ;
|
||||
virtual void Copy( const CSharedObject & soRHS );
|
||||
virtual void Dump() const;
|
||||
// virtual bool BIsNetworkDirty() const { return false; }
|
||||
// virtual void MakeNetworkClean() {}
|
||||
|
||||
const CRecordInfo * GetRecordInfo() const;
|
||||
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
virtual void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
protected:
|
||||
virtual CRecordBase *GetPObject() = 0;
|
||||
const CRecordBase *GetPObject() const { return const_cast<CSchemaSharedObjectBase *>(this)->GetPObject(); }
|
||||
bool BParseFieldsFromMessage( CGCMsgBase *pMsg, const CColumnSet & csFields );
|
||||
CColumnSet GetDatabaseDirtyColumnSet( const CUtlVector< int > &fields ) const;
|
||||
|
||||
// virtual bool BIsFieldDatabaseDirty( int nField ) const = 0;
|
||||
// virtual CColumnSet GetDirtyColumnSet( ) const = 0;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Template for making a shared object that uses a specific Schema
|
||||
// class for its data store.
|
||||
//----------------------------------------------------------------------------
|
||||
template< typename SchObject_t, int nTypeID >
|
||||
class CSchemaSharedObject : public CSchemaSharedObjectBase
|
||||
{
|
||||
public:
|
||||
CSchemaSharedObject()
|
||||
: CSchemaSharedObjectBase( GCSDK::GSchemaFull().GetSchema( SchObject_t::k_iTable ).GetRecordInfo() )
|
||||
{
|
||||
}
|
||||
|
||||
~CSchemaSharedObject()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Ensure this SO is not in any cache, or we have an error.
|
||||
Assert( !GGCBase()->IsSOCached( this, nTypeID ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual int GetTypeID() const { return nTypeID; }
|
||||
|
||||
SchObject_t & Obj() { return m_schObject; }
|
||||
const SchObject_t & Obj() const { return m_schObject; }
|
||||
|
||||
typedef SchObject_t SchObjectType_t;
|
||||
public:
|
||||
const static int k_nTypeID = nTypeID;
|
||||
const static int k_iFieldMax = SchObject_t::k_iFieldMax;
|
||||
|
||||
protected:
|
||||
CRecordBase *GetPObject() { return &m_schObject; }
|
||||
private:
|
||||
SchObject_t m_schObject;
|
||||
};
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
|
||||
#endif //SCHEMASHAREDOBJECT_H
|
||||
926
public/gcsdk/sdocache.h
Normal file
926
public/gcsdk/sdocache.h
Normal file
@@ -0,0 +1,926 @@
|
||||
//====== Copyright Valve Corporation, All rights reserved. ====================
|
||||
//
|
||||
// Purpose: Serialized Digital Object caching and manipulation
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef SBOCACHE_H
|
||||
#define SBOCACHE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier1/utlhashmaplarge.h"
|
||||
#include "tier1/utlqueue.h"
|
||||
#include "tier1/utlvector.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
// Call to register SDOs. All SDO types must be registered before loaded
|
||||
#define REG_SDO( classname ) GSDOCache().RegisterSDO( classname::k_eType, #classname )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Keeps a moving average of a data set
|
||||
//-----------------------------------------------------------------------------
|
||||
template< int SAMPLES >
|
||||
class CMovingAverage
|
||||
{
|
||||
public:
|
||||
CMovingAverage()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
memset( m_rglSamples, 0, sizeof( m_rglSamples ) );
|
||||
m_cSamples = 0;
|
||||
m_lTotal = 0;
|
||||
}
|
||||
|
||||
void AddSample( int64 lSample )
|
||||
{
|
||||
int iIndex = m_cSamples % SAMPLES;
|
||||
m_lTotal += ( lSample - m_rglSamples[iIndex] );
|
||||
m_rglSamples[iIndex] = lSample;
|
||||
m_cSamples++;
|
||||
}
|
||||
|
||||
uint64 GetAveragedSample() const
|
||||
{
|
||||
if ( !m_cSamples )
|
||||
return 0;
|
||||
|
||||
int64 iMax = (int64)MIN( m_cSamples, SAMPLES );
|
||||
return m_lTotal / iMax;
|
||||
}
|
||||
|
||||
private:
|
||||
int64 m_rglSamples[SAMPLES];
|
||||
int64 m_lTotal;
|
||||
uint64 m_cSamples;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Global accessor to the manager
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSDOCache;
|
||||
CSDOCache &GSDOCache();
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: interface to a Database Backed Object
|
||||
//-----------------------------------------------------------------------------
|
||||
class ISDO
|
||||
{
|
||||
public:
|
||||
virtual ~ISDO() {}
|
||||
|
||||
// Identification
|
||||
virtual int GetType() const = 0;
|
||||
virtual uint32 GetHashCode() const = 0;
|
||||
virtual bool IsEqual( const ISDO *pSDO ) const = 0;
|
||||
|
||||
// Ref counting
|
||||
virtual int AddRef() = 0;
|
||||
virtual int Release() = 0;
|
||||
virtual int GetRefCount() = 0;
|
||||
|
||||
// memory usage
|
||||
virtual size_t CubBytesUsed() = 0;
|
||||
|
||||
// Serialization tools
|
||||
virtual bool BReadFromBuffer( const byte *pubData, int cubData ) = 0;
|
||||
virtual void WriteToBuffer( CUtlBuffer &memBuffer ) = 0;
|
||||
|
||||
// memcached batching tools
|
||||
virtual void GetMemcachedKeyName( CUtlString &sName ) = 0;
|
||||
|
||||
// SQL loading
|
||||
virtual bool BYldLoadFromSQL( CUtlVector<ISDO *> &vecSDOToLoad, CUtlVector<bool> &vecResults ) const = 0;
|
||||
|
||||
// post-load initialization (whether loaded from SQL or memcached)
|
||||
virtual void PostLoadInit() = 0;
|
||||
|
||||
// comparison function for validating memcached copies vs SQL copies
|
||||
virtual bool IsIdentical( ISDO *pSDO ) = 0;
|
||||
};
|
||||
|
||||
//**tempcomment**typedef ISDO *(*CreateSDOFunc_t)( uint32 nAccountID );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: base class for a Serialized Digital Object
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int eSDOType, class ProtoMsg>
|
||||
class CBaseSDO : public ISDO
|
||||
{
|
||||
public:
|
||||
typedef KeyType KeyType_t;
|
||||
enum { k_eType = eSDOType };
|
||||
|
||||
CBaseSDO( const KeyType &key ) : m_Key( key ), m_nRefCount( 0 ) {}
|
||||
|
||||
const KeyType &GetKey() const { return m_Key; }
|
||||
|
||||
// ISDO implementation
|
||||
virtual int AddRef();
|
||||
virtual int Release();
|
||||
virtual int GetRefCount();
|
||||
virtual int GetType() const { return eSDOType; }
|
||||
virtual uint32 GetHashCode() const;
|
||||
virtual bool BReadFromBuffer( const byte *pubData, int cubData );
|
||||
virtual void WriteToBuffer( CUtlBuffer &memBuffer );
|
||||
|
||||
// We use protobufs for all serialization
|
||||
virtual void SerializeToProtobuf( ProtoMsg &msg ) = 0;
|
||||
virtual bool DeserializeFromProtobuf( const ProtoMsg &msg ) = 0;
|
||||
|
||||
// default comparison function - override to do your own compare
|
||||
virtual bool IsEqual( const ISDO *pSDO ) const;
|
||||
|
||||
// default load from SQL is no-op as not all types have permanent storage - override to create a
|
||||
// batch load
|
||||
virtual bool BYldLoadFromSQL( CUtlVector<ISDO *> &vecSDOToLoad, CUtlVector<bool> &vecResults ) const;
|
||||
|
||||
// override to do initialization after load
|
||||
virtual void PostLoadInit() {}
|
||||
|
||||
// compares the serialized versions by default. Override to have more specific behavior
|
||||
virtual bool IsIdentical( ISDO *pSDO );
|
||||
|
||||
// tools
|
||||
bool WriteToMemcached();
|
||||
bool DeleteFromMemcached();
|
||||
|
||||
private:
|
||||
int m_nRefCount;
|
||||
KeyType m_Key;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: references to a database-backed object
|
||||
// maintains refcount of the object
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
class CSDORef
|
||||
{
|
||||
T *m_pSDO;
|
||||
public:
|
||||
CSDORef() { m_pSDO = NULL; }
|
||||
explicit CSDORef( CSDORef<T> &SDORef ) { m_pSDO = SDORef.Get(); m_pSDO->AddRef(); }
|
||||
explicit CSDORef( T *pSDO ) { m_pSDO = pSDO; if ( m_pSDO ) m_pSDO->AddRef(); }
|
||||
~CSDORef() { if ( m_pSDO ) m_pSDO->Release(); }
|
||||
|
||||
T *Get() { return m_pSDO; }
|
||||
const T *Get() const { return m_pSDO; }
|
||||
|
||||
T *operator->() { return Get(); }
|
||||
const T *operator->() const { return Get(); }
|
||||
|
||||
operator const T *() const { return m_pSDO; }
|
||||
operator const T *() { return m_pSDO; }
|
||||
operator T *() { return m_pSDO; }
|
||||
|
||||
CSDORef<T> &operator=( T *pSDO ) { if ( m_pSDO ) m_pSDO->Release(); m_pSDO = pSDO; if ( m_pSDO ) m_pSDO->AddRef(); return *this; }
|
||||
|
||||
bool operator !() const { return Get() == NULL; }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: manages a cache of SDO objects
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSDOCache
|
||||
{
|
||||
public:
|
||||
CSDOCache();
|
||||
~CSDOCache();
|
||||
|
||||
// Call to register SDOs. All SDO types must be registered before loaded
|
||||
void RegisterSDO( int nType, const char *pchName );
|
||||
|
||||
// A struct to hold stats for the system. This is generated code in Steam. It would be great to make
|
||||
// it generated code here if we could bring Steam's operational stats system in the GC
|
||||
struct StatsSDOCache_t
|
||||
{
|
||||
uint64 m_cItemsLRUd;
|
||||
uint64 m_cBytesLRUd;
|
||||
uint64 m_cItemsUnreferenced;
|
||||
uint64 m_cBytesUnreferenced;
|
||||
uint64 m_cItemsInCache;
|
||||
uint64 m_cBytesInCacheEst;
|
||||
uint64 m_cItemsQueuedToLoad;
|
||||
uint64 m_cItemsLoadedFromMemcached;
|
||||
uint64 m_cItemsLoadedFromSQL;
|
||||
uint64 m_cItemsFailedLoadFromSQL;
|
||||
uint64 m_cQueuedMemcachedRequests;
|
||||
uint64 m_cQueuedSQLRequests;
|
||||
uint64 m_nSQLBatchSizeAvgx100;
|
||||
uint64 m_nMemcachedBatchSizeAvgx100;
|
||||
uint64 m_cSQLRequestsRejectedTooBusy;
|
||||
uint64 m_cMemcachedRequestsRejectedTooBusy;
|
||||
};
|
||||
|
||||
// loads a SDO, and assigns a reference to it
|
||||
// returns false if the item couldn't be loaded, or timed out loading
|
||||
template<class T>
|
||||
bool BYldLoadSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key, bool *pbTimeoutLoading = NULL );
|
||||
|
||||
// gets access to a SDO, but only if it's currently loaded
|
||||
template<class T>
|
||||
bool BGetLoadedSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key );
|
||||
|
||||
// starts loading a SDO you're going to reference soon with the above BYldLoadSDO()
|
||||
// use this to batch up requests, hinting a set then getting reference to a set is significantly faster
|
||||
template<class T>
|
||||
void HintLoadSDO( const typename T::KeyType_t &key );
|
||||
// as above, but starts load a set
|
||||
template<class T>
|
||||
void HintLoadSDO( const CUtlVector<typename T::KeyType_t> &vecKeys );
|
||||
|
||||
// force a deletes a SDO from the cache - waits until the object is not referenced
|
||||
template<class T>
|
||||
bool BYldDeleteSDO( const typename T::KeyType_t &key, uint64 unMicrosecondsToWaitForUnreferenced );
|
||||
|
||||
// SDO refcount management
|
||||
void OnSDOReferenced( ISDO *pSDO );
|
||||
void OnSDOReleased( ISDO *pSDO );
|
||||
|
||||
// writes a SDO to memcached immediately
|
||||
bool WriteSDOToMemcached( ISDO *pSDO );
|
||||
// delete the SDO record from memcached
|
||||
bool DeleteSDOFromMemcached( ISDO *pSDO );
|
||||
|
||||
// job results
|
||||
void OnSDOLoadSuccess( int eSDO, int iRequestID );
|
||||
void OnMemcachedSDOLoadFailure( int eSDO, int iRequestID );
|
||||
void OnSQLSDOLoadFailure( int eSDO, int iRequestID, bool bSQLLayerSucceeded );
|
||||
void OnMemcachedLoadJobComplete( JobID_t jobID );
|
||||
void OnSQLLoadJobComplete( int eSDO, JobID_t jobID );
|
||||
|
||||
// test access - deletes all unreferenced objects
|
||||
void Flush();
|
||||
|
||||
// stats access
|
||||
StatsSDOCache_t &GetStats() { return m_StatsSDOCache; }
|
||||
int CubReferencedEst(); // number of bytes referenced in the cache
|
||||
|
||||
// prints info about the class
|
||||
void Dump();
|
||||
|
||||
// memcached verification - returns the number of mismatches
|
||||
//**tempcomment** void YldVerifyMemcachedData( CreateSDOFunc_t pCreateSDOFunc, CUtlVector<uint32> &vecIDs, int *pcMatches, int *pcMismatches );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
|
||||
// Functions that need to be in the frame loop
|
||||
virtual bool BFrameFuncRunJobsUntilCompleted( CLimitTimer &limitTimer );
|
||||
virtual bool BFrameFuncRunMemcachedQueriesUntilCompleted( CLimitTimer &limitTimer );
|
||||
virtual bool BFrameFuncRunSQLQueriesOnce( CLimitTimer &limitTimer );
|
||||
|
||||
private:
|
||||
// Custom comparator for our hash map
|
||||
class CDefPISDOEquals
|
||||
{
|
||||
public:
|
||||
CDefPISDOEquals() {}
|
||||
CDefPISDOEquals( int i ) {}
|
||||
inline bool operator()( const ISDO *lhs, const ISDO *rhs ) const { return ( lhs->IsEqual( rhs ) ); }
|
||||
inline bool operator!() const { return false; }
|
||||
};
|
||||
|
||||
class CPISDOHashFunctor
|
||||
{
|
||||
public:
|
||||
uint32 operator()(const ISDO *pSDO ) const { return pSDO->GetHashCode(); }
|
||||
};
|
||||
|
||||
template<class T>
|
||||
int FindLoadedSDO( const typename T::KeyType_t &key );
|
||||
|
||||
template<class T>
|
||||
int QueueLoad( const typename T::KeyType_t &key );
|
||||
int QueueMemcachedLoad( ISDO *pSDO );
|
||||
|
||||
// items already loaded - Maps the SDO to the LRU position
|
||||
CUtlHashMapLarge<ISDO *, int, CDefPISDOEquals, CPISDOHashFunctor> m_mapISDOLoaded;
|
||||
|
||||
// items we have queued to load, in the state of either being loaded from memcached or SQL
|
||||
// maps SDO to a list of jobs waiting on the load
|
||||
CUtlHashMapLarge<ISDO *, CCopyableUtlVector<JobID_t>, CDefPISDOEquals, CPISDOHashFunctor> m_mapQueuedRequests;
|
||||
|
||||
// requests to load from memcached
|
||||
CUtlLinkedList<int, int> m_queueMemcachedRequests;
|
||||
|
||||
// Jobs currently processing memcached load requests
|
||||
CUtlVector<JobID_t> m_vecMemcachedJobs;
|
||||
|
||||
// Loading from SQL is divided by SDO type
|
||||
struct SQLRequestManager_t
|
||||
{
|
||||
// requests to load from SQL. Maps to an ID in the map of queued requests
|
||||
CUtlLinkedList<int, int> m_queueRequestIDsToLoadFromSQL;
|
||||
|
||||
// SQL jobs we have active doing reads for cache items
|
||||
CUtlVector<JobID_t> m_vecSQLJobs;
|
||||
};
|
||||
|
||||
// a queue of requests to load from SQL for each type
|
||||
CUtlHashMapLarge<int, SQLRequestManager_t *> m_mapQueueSQLRequests;
|
||||
|
||||
// jobs to wake up, since we've satisfied their SDO load request
|
||||
struct JobToWake_t
|
||||
{
|
||||
JobID_t m_jobID;
|
||||
bool m_bLoadLayerSuccess;
|
||||
};
|
||||
CUtlLinkedList<JobToWake_t, int> m_queueJobsToContinue;
|
||||
|
||||
struct LRUItem_t
|
||||
{
|
||||
ISDO * m_pSDO;
|
||||
size_t m_cub;
|
||||
};
|
||||
CUtlLinkedList<LRUItem_t, int> m_listLRU;
|
||||
uint32 m_cubLRUItems;
|
||||
void RemoveSDOFromLRU( int iMapSDOLoaded );
|
||||
|
||||
struct TypeStats_t
|
||||
{
|
||||
TypeStats_t()
|
||||
: m_nLoaded( 0 )
|
||||
, m_nRefed( 0 )
|
||||
, m_cubUnrefed( 0 )
|
||||
{}
|
||||
|
||||
CUtlString m_strName;
|
||||
int m_nLoaded;
|
||||
int m_nRefed;
|
||||
int m_cubUnrefed;
|
||||
};
|
||||
|
||||
StatsSDOCache_t m_StatsSDOCache;
|
||||
CMovingAverage<100> m_StatMemcachedBatchSize, m_StatSQLBatchSize;
|
||||
CUtlMap<int, TypeStats_t> m_mapTypeStats;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Definition of CBaseSDO template functions now that CSDOCache is defined and
|
||||
// GSDOCache() can safely be used.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: adds a reference
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
int CBaseSDO<KeyType,ESDOType,ProtoMsg>::AddRef()
|
||||
{
|
||||
if ( ++m_nRefCount == 1 )
|
||||
GSDOCache().OnSDOReferenced( this );
|
||||
|
||||
return m_nRefCount;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: releases a reference
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
int CBaseSDO<KeyType,ESDOType,ProtoMsg>::Release()
|
||||
{
|
||||
DbgVerify( m_nRefCount > 0 );
|
||||
|
||||
int nRefCount = --m_nRefCount;
|
||||
|
||||
if ( nRefCount == 0 )
|
||||
GSDOCache().OnSDOReleased( this );
|
||||
|
||||
return nRefCount;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: ref count
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
int CBaseSDO<KeyType,ESDOType,ProtoMsg>::GetRefCount()
|
||||
{
|
||||
return m_nRefCount;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Hashes the object for insertion into a hashtable
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
uint32 CBaseSDO<KeyType,ESDOType,ProtoMsg>::GetHashCode() const
|
||||
{
|
||||
struct hashcode_t
|
||||
{
|
||||
int m_Type;
|
||||
KeyType_t m_Key;
|
||||
} hashStruct = { ESDOType, m_Key };
|
||||
return PearsonsHashFunctor<hashcode_t>()( hashStruct );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Deserializes the object
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::BReadFromBuffer( const byte *pubData, int cubData )
|
||||
{
|
||||
ProtoMsg msg;
|
||||
if ( !msg.ParseFromArray( pubData, cubData ) )
|
||||
return false;
|
||||
|
||||
if ( !DeserializeFromProtobuf( msg ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Serializes the object
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
void CBaseSDO<KeyType,ESDOType,ProtoMsg>::WriteToBuffer( CUtlBuffer &memBuffer )
|
||||
{
|
||||
ProtoMsg msg;
|
||||
SerializeToProtobuf( msg );
|
||||
CProtoBufSharedObjectHelper::AddProtoBufMessageToBuffer( memBuffer, msg, true );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: does an immediate write of the object to memcached
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::WriteToMemcached()
|
||||
{
|
||||
return GSDOCache().WriteSDOToMemcached( this );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: does an immediate write of the object to memcached
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::DeleteFromMemcached()
|
||||
{
|
||||
return GSDOCache().DeleteSDOFromMemcached( this );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: default equality function - compares type and key
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::IsEqual( const ISDO *pSDO ) const
|
||||
{
|
||||
if ( GetType() != pSDO->GetType() )
|
||||
return false;
|
||||
|
||||
return ( GetKey() == static_cast<const CBaseSDO<KeyType,ESDOType,ProtoMsg> *>( pSDO )->GetKey() );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Batch load a group of SDO's of the same type from SQL.
|
||||
// Default is no-op as not all types have permanent storage.
|
||||
//-----------------------------------------------------------------------------
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::BYldLoadFromSQL( CUtlVector<ISDO *> &vecSDOToLoad, CUtlVector<bool> &vecResults ) const
|
||||
{
|
||||
FOR_EACH_VEC( vecResults, i )
|
||||
{
|
||||
vecResults[i] = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: default validation function - compares serialized versions
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CompareSDOObjects( ISDO *pSDO1, ISDO *pSDO2 );
|
||||
|
||||
template<typename KeyType, int ESDOType, class ProtoMsg>
|
||||
bool CBaseSDO<KeyType,ESDOType,ProtoMsg>::IsIdentical( ISDO *pSDO )
|
||||
{
|
||||
return CompareSDOObjects( this, pSDO );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Finds a loaded SDO in memory. Returns the index of the object
|
||||
// into the loaded SDOs map
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
int CSDOCache::FindLoadedSDO( const typename T::KeyType_t &key )
|
||||
{
|
||||
// see if we have it in cache first
|
||||
T probe( key );
|
||||
return m_mapISDOLoaded.Find( &probe );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Queues loading an SDO. Returns the index of the entry in the
|
||||
// load queue
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
int CSDOCache::QueueLoad( const typename T::KeyType_t &key )
|
||||
{
|
||||
T probe( key );
|
||||
int iMap = m_mapQueuedRequests.Find( &probe );
|
||||
if ( m_mapQueuedRequests.IsValidIndex( iMap ) )
|
||||
return iMap;
|
||||
|
||||
return QueueMemcachedLoad( new T( key ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Preloads the object into the local cache
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
void CSDOCache::HintLoadSDO( const typename T::KeyType_t &key )
|
||||
{
|
||||
// see if we have it in cache first
|
||||
if ( !m_mapISDOLoaded.IsValidIndex( FindLoadedSDO<T>( key ) ) )
|
||||
{
|
||||
QueueLoad<T>( key );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Preloads a set set of objects into the local cache
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
void CSDOCache::HintLoadSDO( const CUtlVector<typename T::KeyType_t> &vecKeys )
|
||||
{
|
||||
FOR_EACH_VEC( vecKeys, i )
|
||||
{
|
||||
HintLoadSDO<T>( vecKeys[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns an already-loaded SDO
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
bool CSDOCache::BGetLoadedSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key )
|
||||
{
|
||||
int iMap = FindLoadedSDO<T>( key );
|
||||
if ( !m_mapISDOLoaded.IsValidIndex( iMap ) )
|
||||
return false;
|
||||
|
||||
*pPSDORef = assert_cast<T*>( m_mapISDOLoaded.Key( iMap ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads the object into memory
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
bool CSDOCache::BYldLoadSDO( CSDORef<T> *pPSDORef, const typename T::KeyType_t &key, bool *pbTimeoutLoading /* = NULL */ )
|
||||
{
|
||||
VPROF_BUDGET( "CSDOCache::BYldLoadSDO", VPROF_BUDGETGROUP_STEAM );
|
||||
if ( pbTimeoutLoading )
|
||||
*pbTimeoutLoading = false;
|
||||
|
||||
// Clear the current object the ref is holding
|
||||
*pPSDORef = NULL;
|
||||
|
||||
// see if we have it in cache first
|
||||
if ( BGetLoadedSDO( pPSDORef, key ) )
|
||||
return true;
|
||||
|
||||
// otherwise batch it for load
|
||||
int iMap = QueueLoad<T>( key );
|
||||
|
||||
// make sure we could queue it
|
||||
if ( !m_mapQueuedRequests.IsValidIndex( iMap ) )
|
||||
return false;
|
||||
|
||||
// add the current job to this list waiting for the object to load
|
||||
m_mapQueuedRequests[iMap].AddToTail( GJobCur().GetJobID() );
|
||||
|
||||
// wait for it to load (loader will signal our job when done)
|
||||
if ( !GJobCur().BYieldingWaitForWorkItem() )
|
||||
{
|
||||
if ( pbTimeoutLoading )
|
||||
*pbTimeoutLoading = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// should be loaded - look up in the load map and try again
|
||||
bool bRet = BGetLoadedSDO( pPSDORef, key );
|
||||
Assert( bRet );
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: reloads an existing element from the SQL DB
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
bool CSDOCache::BYldDeleteSDO( const typename T::KeyType_t &key, uint64 unMicrosecondsToWaitForUnreferenced )
|
||||
{
|
||||
// see if we have it in cache first
|
||||
int iMap = FindLoadedSDO<T>( key );
|
||||
if ( !m_mapISDOLoaded.IsValidIndex( iMap ) )
|
||||
{
|
||||
T temp( key );
|
||||
temp.DeleteFromMemcached();
|
||||
return true; /* we're good, it's not loaded */
|
||||
}
|
||||
|
||||
assert_cast<T *>(m_mapISDOLoaded.Key( iMap ))->DeleteFromMemcached();
|
||||
|
||||
// check the ref count
|
||||
int64 cAttempts = MAX( 1LL, (int64)(unMicrosecondsToWaitForUnreferenced / k_cMicroSecPerShellFrame) );
|
||||
while ( cAttempts-- > 0 )
|
||||
{
|
||||
if ( 0 == m_mapISDOLoaded.Key( iMap )->GetRefCount() )
|
||||
{
|
||||
// delete the object
|
||||
Assert( m_listLRU.IsValidIndex( m_mapISDOLoaded[iMap] ) );
|
||||
|
||||
int iMapStats = m_mapTypeStats.Find( m_mapISDOLoaded.Key( iMap )->GetType() );
|
||||
if ( m_mapTypeStats.IsValidIndex( iMapStats ) )
|
||||
{
|
||||
m_mapTypeStats[iMapStats].m_nLoaded--;
|
||||
}
|
||||
|
||||
RemoveSDOFromLRU( iMap );
|
||||
ISDO *pSDO = m_mapISDOLoaded.Key( iMap );
|
||||
m_mapISDOLoaded.RemoveAt( iMap );
|
||||
delete pSDO;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
GJobCur().BYieldingWaitOneFrame();
|
||||
}
|
||||
}
|
||||
|
||||
// couldn't reload
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: A class to factor out the common code in most SDO SQL loading funcitons
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
class CSDOSQLLoadHelper
|
||||
{
|
||||
public:
|
||||
// Initializes with the vector of objects being loaded
|
||||
CSDOSQLLoadHelper( const CUtlVector<ISDO *> *vecSDOToLoad, const char *pchProfileName );
|
||||
|
||||
// Loads all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
|
||||
template<class SCH>
|
||||
bool BYieldingExecuteSingleTable( int nFieldID, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults );
|
||||
// Loads the specified columns for all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
|
||||
template<class SCH>
|
||||
bool BYieldingExecuteSingleTable( int nFieldID, const CColumnSet &csetRead, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults );
|
||||
|
||||
// Functions to load rows from more than one table at a time
|
||||
|
||||
// Loads all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
|
||||
template<class SCH>
|
||||
void AddTableToQuery( int nFieldID );
|
||||
|
||||
// Loads the specified columns for all rows in the SCH table whose field nFieldID match the key of an SDO being loaded
|
||||
template<class SCH>
|
||||
void AddTableToQuery( int nFieldID, const CColumnSet &csetRead );
|
||||
|
||||
// Executes the mutli-table query
|
||||
bool BYieldingExecute();
|
||||
|
||||
// Gets the results for a table from a multi-table query
|
||||
template<class SCH>
|
||||
bool BGetResults( int nQuery, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults );
|
||||
|
||||
private:
|
||||
CUtlVector<typename T::KeyType_t> m_vecKeys;
|
||||
CSQLAccess m_sqlAccess;
|
||||
|
||||
struct Query_t
|
||||
{
|
||||
Query_t( const CColumnSet &columnSet, int nKeyCol ) : m_ColumnSet( columnSet ), m_nKeyCol( nKeyCol ) {}
|
||||
CColumnSet m_ColumnSet;
|
||||
int m_nKeyCol;
|
||||
};
|
||||
|
||||
CUtlVector<Query_t> m_vecQueries;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Constructor. Initializes with the vector of objects being loaded
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
CSDOSQLLoadHelper<T>::CSDOSQLLoadHelper( const CUtlVector<ISDO *> *vecSDOToLoad, const char *pchProfileName )
|
||||
: m_vecKeys( 0, vecSDOToLoad->Count() )
|
||||
{
|
||||
FOR_EACH_VEC( *vecSDOToLoad, i )
|
||||
{
|
||||
m_vecKeys.AddToTail( ( (T*)vecSDOToLoad->Element( i ) )->GetKey() );
|
||||
}
|
||||
|
||||
Assert( m_vecKeys.Count() > 0 );
|
||||
DbgVerify( m_sqlAccess.BBeginTransaction( pchProfileName ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads all rows in the SCH table whose field nFieldID match the
|
||||
// key of an SDO being loaded.
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
template<class SCH>
|
||||
bool CSDOSQLLoadHelper<T>::BYieldingExecuteSingleTable( int nFieldID, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults )
|
||||
{
|
||||
static const CColumnSet cSetRead = CColumnSet::Full<SCH>();
|
||||
return BYieldingExecuteSingleTable( nFieldID, cSetRead, pMapResults );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads the specified columns for all rows in the SCH table whose
|
||||
// field nFieldID match the key of an SDO being loaded
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
template<class SCH>
|
||||
bool CSDOSQLLoadHelper<T>::BYieldingExecuteSingleTable( int nFieldID, const CColumnSet &csetRead, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults )
|
||||
{
|
||||
AddTableToQuery<SCH>( nFieldID, csetRead );
|
||||
if ( !BYieldingExecute() )
|
||||
return false;
|
||||
|
||||
return BGetResults<SCH>( 0, pMapResults );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads all rows in the SCH table whose field nFieldID match the key
|
||||
// of an SDO being loaded
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
template<class SCH>
|
||||
void CSDOSQLLoadHelper<T>::AddTableToQuery( int nFieldID )
|
||||
{
|
||||
static const CColumnSet cSetRead = CColumnSet::Full<SCH>();
|
||||
AddTableToQuery<SCH>( nFieldID, cSetRead );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Loads the specified columns for all rows in the SCH table whose
|
||||
// field nFieldID match the key of an SDO being loaded
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
template<class SCH>
|
||||
void CSDOSQLLoadHelper<T>::AddTableToQuery( int nFieldID, const CColumnSet &csetRead )
|
||||
{
|
||||
Assert( csetRead.GetRecordInfo() == GSchemaFull().GetSchema( SCH::k_iTable ).GetRecordInfo() );
|
||||
|
||||
// Bind the params
|
||||
FOR_EACH_VEC( m_vecKeys, i )
|
||||
{
|
||||
m_sqlAccess.AddBindParam( m_vecKeys[i] );
|
||||
}
|
||||
|
||||
// Build the query
|
||||
CFmtStr1024 sCommand;
|
||||
const char *pchColumnName = GSchemaFull().GetSchema( SCH::k_iTable ).GetRecordInfo()->GetColumnInfo( nFieldID ).GetName();
|
||||
|
||||
BuildSelectStatementText( &sCommand, csetRead );
|
||||
sCommand.AppendFormat( " WHERE %s IN (%.*s)", pchColumnName, max( 0, ( m_vecKeys.Count() * 2 ) - 1 ), GetInsertArgString() );
|
||||
|
||||
// Execute. Because we're in a transaction this will delay to the commit
|
||||
DbgVerify( m_sqlAccess.BYieldingExecute( NULL, sCommand ) );
|
||||
|
||||
m_vecQueries.AddToTail( Query_t( csetRead, nFieldID ) );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Executes the mutli-table query
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
bool CSDOSQLLoadHelper<T>::BYieldingExecute()
|
||||
{
|
||||
if ( 0 == m_vecKeys.Count() )
|
||||
{
|
||||
m_sqlAccess.RollbackTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !m_sqlAccess.BCommitTransaction() )
|
||||
return false;
|
||||
|
||||
Assert( (uint32)m_vecQueries.Count() == m_sqlAccess.GetResultSetCount() );
|
||||
return (uint32)m_vecQueries.Count() == m_sqlAccess.GetResultSetCount();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Gets the results for a table from a multi-table query
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
template<class SCH>
|
||||
bool CSDOSQLLoadHelper<T>::BGetResults( int nQuery, CUtlMap<typename T::KeyType_t, CCopyableUtlVector<SCH>, int> *pMapResults )
|
||||
{
|
||||
pMapResults->RemoveAll();
|
||||
|
||||
IGCSQLResultSetList *pResults = m_sqlAccess.GetResults();
|
||||
Assert( pResults && nQuery >= 0 && (uint32)nQuery < pResults->GetResultSetCount() && pResults->GetResultSetCount() == (uint32)m_vecQueries.Count() );
|
||||
if ( NULL == pResults || nQuery < 0 || (uint32)nQuery >= pResults->GetResultSetCount() || pResults->GetResultSetCount() != (uint32)m_vecQueries.Count() )
|
||||
return false;
|
||||
|
||||
Assert( m_vecQueries[nQuery].m_ColumnSet.GetRecordInfo()->GetTableID() == SCH::k_iTable );
|
||||
if ( m_vecQueries[nQuery].m_ColumnSet.GetRecordInfo()->GetTableID() != SCH::k_iTable )
|
||||
return false;
|
||||
|
||||
CUtlVector<SCH> vecResults;
|
||||
if ( !CopyResultToSchVector( pResults->GetResultSet( nQuery ), m_vecQueries[nQuery].m_ColumnSet, &vecResults ) )
|
||||
return false;
|
||||
|
||||
// Make a map that counts how many are in each key so we can intelligently preallocate the result map
|
||||
// Copying around vectors of large SCHs could get expensive
|
||||
CUtlMap<typename T::KeyType_t, int, int > mapCounts( DefLessFunc( T::KeyType_t ) );
|
||||
FOR_EACH_VEC( vecResults, iVec )
|
||||
{
|
||||
uint8 *pubData;
|
||||
uint32 cubData;
|
||||
if ( !vecResults[iVec].BGetField( m_vecQueries[nQuery].m_nKeyCol, &pubData, &cubData ) )
|
||||
return false;
|
||||
|
||||
Assert( cubData == sizeof( T::KeyType_t ) );
|
||||
if ( cubData != sizeof( T::KeyType_t ) )
|
||||
return false;
|
||||
|
||||
const T::KeyType_t &key = *((T::KeyType_t *)pubData);
|
||||
int iMapCounts = mapCounts.Find( key );
|
||||
if ( mapCounts.IsValidIndex( iMapCounts ) )
|
||||
{
|
||||
mapCounts[iMapCounts]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
mapCounts.Insert( key, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
// Preallocate the results map
|
||||
pMapResults->EnsureCapacity( mapCounts.Count() );
|
||||
FOR_EACH_MAP_FAST( mapCounts, iMapCount )
|
||||
{
|
||||
int iMapResult = pMapResults->Insert( mapCounts.Key( iMapCount ) );
|
||||
pMapResults->Element( iMapResult ).EnsureCapacity( mapCounts[iMapCount] );
|
||||
}
|
||||
|
||||
FOR_EACH_VEC( vecResults, iVec )
|
||||
{
|
||||
uint8 *pubData;
|
||||
uint32 cubData;
|
||||
if ( !vecResults[iVec].BGetField( m_vecQueries[nQuery].m_nKeyCol, &pubData, &cubData ) )
|
||||
return false;
|
||||
|
||||
Assert( cubData == sizeof( T::KeyType_t ) );
|
||||
if ( cubData != sizeof( T::KeyType_t ) )
|
||||
return false;
|
||||
|
||||
const T::KeyType_t &key = *((T::KeyType_t *)pubData);
|
||||
int iMapResult = pMapResults->Find( key );
|
||||
Assert( pMapResults->IsValidIndex( iMapResult ) );
|
||||
if ( !pMapResults->IsValidIndex( iMapResult ) )
|
||||
continue;
|
||||
|
||||
pMapResults->Element( iMapResult ).AddToTail( vecResults[iVec] );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // SDOCACHE_H
|
||||
140
public/gcsdk/sharedobject.h
Normal file
140
public/gcsdk/sharedobject.h
Normal file
@@ -0,0 +1,140 @@
|
||||
//====== 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
|
||||
|
||||
#include "utlsortvector.h"
|
||||
#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:
|
||||
virtual ~CSharedObject() {}
|
||||
|
||||
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 BAddToMessage( std::string *pBuffer ) const = 0;
|
||||
virtual bool BAddDestroyToMessage( std::string *pBuffer ) const = 0;
|
||||
|
||||
bool BIsKeyEqual( const CSharedObject & soRHS ) const;
|
||||
|
||||
static void RegisterFactory( int nTypeID, SOCreationFunc_t fnFactory, uint32 unFlags, const char *pchClassName, const char* pszBuildCacheName, const char* pszCreateName, const char* pszUpdateName );
|
||||
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 BParseFromMemcached( CUtlBuffer & buffer ) { return false; }
|
||||
virtual bool BAddToMemcached( CUtlBuffer & bufOutput ) const { return false; }
|
||||
|
||||
bool BSendCreateToSteamIDs( const CUtlVector<CSteamID> & vecRecipients, const SOID_t ownerID, uint64 ulVersion ) const;
|
||||
bool BSendDestroyToSteamIDs( const CUtlVector<CSteamID> & vecRecipients, const SOID_t ownerID, uint64 ulVersion ) const;
|
||||
|
||||
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
|
||||
|
||||
private:
|
||||
struct SharedObjectInfo_t
|
||||
{
|
||||
int m_nID;
|
||||
uint32 m_unFlags;
|
||||
SOCreationFunc_t m_pFactoryFunction;
|
||||
const char *m_pchClassName;
|
||||
const char *m_pchBuildCacheSubNodeName;
|
||||
const char *m_pchUpdateNodeName;
|
||||
const char *m_pchCreateNodeName;
|
||||
};
|
||||
|
||||
//compare class that supports sorting shared objects themselves, as well as searching based upon an integer key value
|
||||
class CCompareSharedObject
|
||||
{
|
||||
public:
|
||||
bool Less( const SharedObjectInfo_t& lhs, const SharedObjectInfo_t& rhs, void* ) const { return lhs.m_nID < rhs.m_nID; }
|
||||
bool Less( int lhs, const SharedObjectInfo_t& rhs, void* ) const { return lhs < rhs.m_nID; }
|
||||
bool Less( const SharedObjectInfo_t& lhs, int rhs, void* ) const { return lhs.m_nID < rhs; }
|
||||
};
|
||||
|
||||
typedef CUtlSortVector< SharedObjectInfo_t, CCompareSharedObject > TVecFactories;
|
||||
static TVecFactories sm_vecFactories;
|
||||
static const SharedObjectInfo_t* FindSharedObjectInfo( int nTypeID );
|
||||
|
||||
public:
|
||||
static TVecFactories & GetFactories() { return sm_vecFactories; }
|
||||
};
|
||||
|
||||
typedef CUtlVectorFixedGrowable<CSharedObject *, 1> CSharedObjectVec;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: Templatized function to use as a factory method for
|
||||
// CSharedObject subclasses
|
||||
//----------------------------------------------------------------------------
|
||||
template<typename SharedObjectSubclass_t>
|
||||
CSharedObject *CreateSharedObjectSubclass()
|
||||
{
|
||||
return new SharedObjectSubclass_t();
|
||||
}
|
||||
|
||||
//internal utility to expand the provided shared object class name into all the names needed for the node
|
||||
#define REG_SHARED_OBJECT_NAMES_INTERNAL( name ) #name, "BuildCacheSubscribed(" #name ")", "Create(" #name ")", "Update(" #name ")"
|
||||
|
||||
#ifdef GC
|
||||
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass, flags ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, (flags), REG_SHARED_OBJECT_NAMES_INTERNAL( derivedClass ) )
|
||||
#else
|
||||
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, 0, REG_SHARED_OBJECT_NAMES_INTERNAL( derivedClass ) )
|
||||
#endif
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif //SHAREDOBJECT_H
|
||||
114
public/gcsdk/sharedobjectcache.h
Normal file
114
public/gcsdk/sharedobjectcache.h
Normal file
@@ -0,0 +1,114 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Base class for objects that are kept in synch between client and server
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef SHAREDOBJECTCACHE_H
|
||||
#define SHAREDOBJECTCACHE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "soid.h"
|
||||
#include "sharedobject.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Purpose: The part of a shared object cache that handles all objects of a
|
||||
// single type.
|
||||
//----------------------------------------------------------------------------
|
||||
class CSharedObjectTypeCache
|
||||
{
|
||||
public:
|
||||
CSharedObjectTypeCache( int nTypeID );
|
||||
virtual ~CSharedObjectTypeCache();
|
||||
|
||||
int GetTypeID() const { return m_nTypeID; }
|
||||
uint32 GetCount() const { return m_vecObjects.Count(); }
|
||||
CSharedObject *GetObject( uint32 nObj ) { return m_vecObjects[nObj]; }
|
||||
const CSharedObject *GetObject( uint32 nObj ) const { return m_vecObjects[nObj]; }
|
||||
|
||||
virtual bool AddObject( CSharedObject *pObject );
|
||||
virtual bool AddObjectClean( CSharedObject *pObject );
|
||||
virtual CSharedObject *RemoveObject( const CSharedObject & soIndex );
|
||||
virtual void RemoveAllObjectsWithoutDeleting();
|
||||
|
||||
virtual void EnsureCapacity( uint32 nItems );
|
||||
|
||||
CSharedObject *FindSharedObject( const CSharedObject & soIndex );
|
||||
const CSharedObject *FindSharedObject( const CSharedObject & soIndex ) const;
|
||||
|
||||
virtual void Dump() const;
|
||||
|
||||
protected:
|
||||
CSharedObject *RemoveObjectByIndex( uint32 nObj );
|
||||
bool HasElement( CSharedObject *pSO ) const { return m_vecObjects.HasElement( pSO ); }
|
||||
|
||||
private:
|
||||
int FindSharedObjectIndex( const CSharedObject & soIndex ) const;
|
||||
|
||||
CSharedObjectVec m_vecObjects;
|
||||
int m_nTypeID;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// 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 CSharedObjectCache
|
||||
{
|
||||
public:
|
||||
CSharedObjectCache();
|
||||
virtual ~CSharedObjectCache();
|
||||
|
||||
virtual SOID_t GetOwner() const = 0;
|
||||
|
||||
virtual bool AddObject( CSharedObject *pSharedObject );
|
||||
virtual bool AddObjectClean( CSharedObject *pSharedObject );
|
||||
virtual CSharedObject *RemoveObject( const CSharedObject & soIndex );
|
||||
virtual bool RemoveAllObjectsWithoutDeleting();
|
||||
|
||||
//called to find the type cache for the specified class ID. This will return NULL if one does not exist
|
||||
const CSharedObjectTypeCache *FindBaseTypeCache( int nClassID ) const;
|
||||
//called to find the type cache for the specified class ID. This will return NULL if one does not exist
|
||||
CSharedObjectTypeCache *FindBaseTypeCache( int nClassID );
|
||||
//called to create the specified class ID. If one exists, this is the same as find, otherwise one will be constructed
|
||||
CSharedObjectTypeCache *CreateBaseTypeCache( int nClassID );
|
||||
|
||||
|
||||
CSharedObject *FindSharedObject( const CSharedObject & soIndex );
|
||||
const CSharedObject *FindSharedObject( const CSharedObject & soIndex ) const;
|
||||
|
||||
void SetVersion( uint64 ulVersion ) { m_ulVersion = ulVersion; }
|
||||
uint64 GetVersion() const { return m_ulVersion; }
|
||||
virtual void MarkDirty() {}
|
||||
|
||||
virtual void Dump() const;
|
||||
|
||||
protected:
|
||||
virtual CSharedObjectTypeCache *AllocateTypeCache( int nClassID ) const = 0;
|
||||
CSharedObjectTypeCache *GetTypeCacheByIndex( int nIndex ) { return m_CacheObjects.IsValidIndex( nIndex ) ? m_CacheObjects.Element( nIndex ) : NULL; }
|
||||
int GetTypeCacheCount() const { return m_CacheObjects.Count(); }
|
||||
|
||||
int FirstTypeCacheIndex() const { return m_CacheObjects.Count() > 0 ? 0 : m_CacheObjects.InvalidIndex(); }
|
||||
int NextTypeCacheIndex( int iCurrent ) const { return ( iCurrent + 1 < m_CacheObjects.Count() ) ? iCurrent + 1 : m_CacheObjects.InvalidIndex(); }
|
||||
int InvalidTypeCacheIndex() const { return m_CacheObjects.InvalidIndex(); }
|
||||
|
||||
uint64 m_ulVersion;
|
||||
private:
|
||||
CUtlVector< CSharedObjectTypeCache * > m_CacheObjects;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
|
||||
#endif //SHAREDOBJECTCACHE_H
|
||||
|
||||
119
public/gcsdk/sharedobjecttransaction.h
Normal file
119
public/gcsdk/sharedobjecttransaction.h
Normal file
@@ -0,0 +1,119 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Base class for transactions that modify a CGCSharedObjectCache and the database
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef SHAREDOBJECTTRANSACTION_H
|
||||
#define SHAREDOBJECTTRANSACTION_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CSharedObjectTransaction
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor that will begin a transaction
|
||||
* @param sqlAccess
|
||||
* @param pName
|
||||
*/
|
||||
CSharedObjectTransaction( CSQLAccess &sqlAccess, const char *pName );
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~CSharedObjectTransaction();
|
||||
|
||||
/**
|
||||
* Adds an object that exists in the given CGCSharedObjectCache to be managed in this transaction.
|
||||
* Call this before making any modifications to the object
|
||||
* @param pSOCache the owner CGCSharedObjectCache
|
||||
* @param pObject the object that will be modified
|
||||
*/
|
||||
bool AddManagedObject( CGCSharedObjectCache *pSOCache, CSharedObject *pObject );
|
||||
|
||||
/**
|
||||
* Adds a brand new object to the given CGCSharedObjectCache
|
||||
* @param pSOCache the owner CGCSharedObjectCache
|
||||
* @param pObject the newly created object
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool BAddNewObject( CGCSharedObjectCache *pSOCache, CSharedObject *pObject, bool bAddToDatabase = true );
|
||||
|
||||
/**
|
||||
* Removes an existing object from the CGCSharedObjectCache
|
||||
* @param pSOCache the owner CGCSharedObjectCache
|
||||
* @param pObject the object to be removed from the CGCSharedObjectCache
|
||||
* @param bRemoveFromDatabase whether to remove the item from the database or not
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool BRemoveObject( CGCSharedObjectCache *pSOCache, CSharedObject *pObject );
|
||||
|
||||
/**
|
||||
* Marks in the transaction that the object was modified. The object must have been previously added via
|
||||
* the AddManagedObject() call in order for the object to be marked dirty. If the object is new to the
|
||||
* CGCSharedObjectCache, then calling this will return false (which is not necessarily an error)
|
||||
*
|
||||
* @param pObject the object that will be modified
|
||||
* @param unFieldIdx the field that was changed
|
||||
* @return true if the field was marked dirty, false otherwise.
|
||||
*/
|
||||
bool BModifiedObject( CSharedObject *pObject, uint32 unFieldIdx );
|
||||
|
||||
/**
|
||||
* @param pSOCache
|
||||
* @param soIndex
|
||||
* @return the CSharedObject that matches either in the CGCSharedObjectCache or to be added
|
||||
*/
|
||||
CSharedObject *FindSharedObject( CGCSharedObjectCache *pSOCache, const CSharedObject &soIndex );
|
||||
|
||||
/**
|
||||
* Rolls back any changes made to the objects in-memory and in the database
|
||||
*
|
||||
* This function should not be made virtual -- it's called from within the destructor.
|
||||
*/
|
||||
void Rollback();
|
||||
|
||||
/**
|
||||
* Commits any changes to the database and also to memory
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool BYieldingCommit( bool bAllowEmpty = false );
|
||||
|
||||
/**
|
||||
* @return GCSDK::CSQLAccess associated with this transaction
|
||||
*/
|
||||
CSQLAccess &GetSQLAccess() { return m_sqlAccess; }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Reverts all in-memory modifications and deletes all newly created objects.
|
||||
*
|
||||
* This function should not be made virtual -- it's called from within the destructor.
|
||||
*/
|
||||
void Undo();
|
||||
|
||||
struct undoinfo_t
|
||||
{
|
||||
CSharedObject *pObject;
|
||||
CGCSharedObjectCache *pSOCache;
|
||||
CSharedObject *pOriginalCopy;
|
||||
bool bAddToDatabase;
|
||||
};
|
||||
|
||||
// variables
|
||||
CUtlVector< undoinfo_t > m_vecObjects_Added;
|
||||
CUtlVector< undoinfo_t > m_vecObjects_Removed;
|
||||
CUtlVector< undoinfo_t > m_vecObjects_Modified;
|
||||
CSQLAccess &m_sqlAccess;
|
||||
}; // class CSharedObjectTransaction
|
||||
|
||||
}; // namespace GCSDK
|
||||
|
||||
#endif // SHAREDOBJECTTRANSACTION_H
|
||||
157
public/gcsdk/soid.h
Normal file
157
public/gcsdk/soid.h
Normal file
@@ -0,0 +1,157 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Ownership id for a shared object cache
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef SOID_H
|
||||
#define SOID_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "steam/steamclientpublic.h"
|
||||
|
||||
class CMsgSOIDOwner;
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Shared type for object caches. This can hold SteamIDs, LobbyIDs, PartyIds,
|
||||
// etc. Make sure they don't conflict!
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct SOID_t;
|
||||
|
||||
const uint32 k_SOID_Type_SteamID = 1;
|
||||
const uint32 k_SOID_Type_PartyGroupID = 2;
|
||||
const uint32 k_SOID_Type_LobbyGroupID = 3;
|
||||
const uint32 k_SOID_Type_PartyInvite = 4;
|
||||
const uint32 k_SOID_Type_CheatReport = 5;
|
||||
const uint32 k_SOID_Type_NqmmRating = 6;
|
||||
|
||||
struct SOIDRender_t
|
||||
{
|
||||
explicit SOIDRender_t( const SOID_t id );
|
||||
const char *String() const;
|
||||
|
||||
//the buffer that is formatted into (should be large enough to hold the string representation of the type and the value)
|
||||
static const uint32 k_cBufLen = 128;
|
||||
char m_buf[ k_cBufLen ];
|
||||
|
||||
//a utility class that is intended to be defined in a source file that will handle registering
|
||||
//the provided name and whether or not it should be displayed as a steam ID with the lock
|
||||
class CAutoRegisterName
|
||||
{
|
||||
public:
|
||||
CAutoRegisterName( uint16 nType, const char* pszDefaultString, bool bDisplaySteamID = false );
|
||||
};
|
||||
|
||||
static const char *GetName( uint32 nType );
|
||||
};
|
||||
|
||||
struct SOID_t
|
||||
{
|
||||
SOID_t()
|
||||
: m_type( 0 )
|
||||
, m_id( 0 )
|
||||
, m_padding( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
SOID_t( uint32 type, uint64 id )
|
||||
: m_type( type )
|
||||
, m_id( id )
|
||||
, m_padding( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
// Conversion from a SteamID
|
||||
SOID_t( CSteamID steamID )
|
||||
: m_type( k_SOID_Type_SteamID )
|
||||
, m_id( steamID.ConvertToUint64() )
|
||||
, m_padding( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
//initializes the soid fields
|
||||
void Init( uint32 type, uint64 id )
|
||||
{
|
||||
m_type = type;
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
// Conversion from a protobuf version
|
||||
SOID_t( const CMsgSOIDOwner &msgSOIDOwner );
|
||||
|
||||
void ToMsgSOIDOwner( CMsgSOIDOwner *pMsgSOIDOwner ) const;
|
||||
|
||||
uint64 ID() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
uint32 Type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
bool IsValid()
|
||||
{
|
||||
return m_type != 0;
|
||||
}
|
||||
|
||||
bool operator==( const SOID_t &rhs ) const
|
||||
{
|
||||
return m_type == rhs.m_type && m_id == rhs.m_id;
|
||||
}
|
||||
|
||||
bool operator!=( const SOID_t &rhs ) const
|
||||
{
|
||||
return m_type != rhs.m_type || m_id != rhs.m_id;
|
||||
}
|
||||
|
||||
bool operator<( const SOID_t &rhs ) const
|
||||
{
|
||||
if ( m_type == rhs.m_type )
|
||||
{
|
||||
return m_id < rhs.m_id;
|
||||
}
|
||||
return m_type < rhs.m_type;
|
||||
}
|
||||
|
||||
SOIDRender_t GetRender() const
|
||||
{
|
||||
return SOIDRender_t( *this );
|
||||
}
|
||||
|
||||
uint64 m_id;
|
||||
uint32 m_type;
|
||||
uint32 m_padding; // so structure is 16 bytes
|
||||
};
|
||||
|
||||
inline const char *SOIDRender_t::String() const
|
||||
{
|
||||
return m_buf;
|
||||
}
|
||||
|
||||
inline SOID_t GetSOIDFromSteamID( CSteamID steamID )
|
||||
{
|
||||
return SOID_t( k_SOID_Type_SteamID, steamID.ConvertToUint64() );
|
||||
}
|
||||
|
||||
inline CSteamID GetSteamIDFromSOID( SOID_t ID )
|
||||
{
|
||||
if ( ID.Type() == k_SOID_Type_SteamID )
|
||||
{
|
||||
return CSteamID( ID.ID() );
|
||||
}
|
||||
return k_steamIDNil;
|
||||
}
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
|
||||
#endif //SOID_H
|
||||
|
||||
261
public/gcsdk/sqlaccess/columnset.h
Normal file
261
public/gcsdk/sqlaccess/columnset.h
Normal file
@@ -0,0 +1,261 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: Sets of columns in queries
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef COLUMNSET_H
|
||||
#define COLUMNSET_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets of columns in queries
|
||||
//-----------------------------------------------------------------------------
|
||||
class CColumnSet
|
||||
{
|
||||
public:
|
||||
CColumnSet( const CRecordInfo *pRecordInfo );
|
||||
CColumnSet( const CRecordInfo *pRecordInfo, int nColumn );
|
||||
CColumnSet( const CColumnSet & rhs );
|
||||
CColumnSet & operator=( const CColumnSet & rhs );
|
||||
const CColumnSet operator+( const CColumnSet & rhs ) const;
|
||||
CColumnSet & operator+=( const CColumnSet & rhs );
|
||||
|
||||
bool BAddColumn( int nColumn );
|
||||
bool BRemoveColumn( int nColumn );
|
||||
bool IsSet( int nColumn ) const;
|
||||
bool IsEmpty() const { return m_vecColumns.Count() == 0;}
|
||||
|
||||
uint32 GetColumnCount() const;
|
||||
int GetColumn( int nIndex ) const;
|
||||
const CColumnInfo & GetColumnInfo( int nIndex ) const;
|
||||
|
||||
const CRecordInfo *GetRecordInfo() const { return m_pRecordInfo; }
|
||||
|
||||
// putting column sets in messages
|
||||
void AddToMessage( CGCMsgBase *pMsg ) const;
|
||||
bool BParseFromMessage( CGCMsgBase *pMsg );
|
||||
|
||||
void MakeEmpty();
|
||||
void MakeFull();
|
||||
void MakeInsertable();
|
||||
void MakeNoninsertable();
|
||||
void MakePrimaryKey();
|
||||
void MakeInverse( const CColumnSet & columnSet );
|
||||
|
||||
template< typename TSchClass >
|
||||
static CColumnSet Empty();
|
||||
template< typename TSchClass >
|
||||
static CColumnSet Full();
|
||||
template< typename TSchClass >
|
||||
static CColumnSet Insertable();
|
||||
template< typename TSchClass >
|
||||
static CColumnSet Noninsertable();
|
||||
template< typename TSchClass >
|
||||
static CColumnSet PrimaryKey();
|
||||
static CColumnSet Inverse( const CColumnSet & columnSet );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName );
|
||||
#endif
|
||||
|
||||
private:
|
||||
CUtlVector<int> m_vecColumns;
|
||||
const CRecordInfo *m_pRecordInfo;
|
||||
};
|
||||
|
||||
// Usage notes:
|
||||
// The fields in a column set are order-dependent, and must match the order of the fields in
|
||||
// the query used to generate the data. The code that reads values doesn't do any fancy
|
||||
// name-matching and will copy values to incorrect locations silently if there is a
|
||||
// disagreement between the fields in the query and the fields in the column set.
|
||||
//
|
||||
// Examples:
|
||||
// // This is broken.
|
||||
// query = "SELECT * FROM Items";
|
||||
// columnSet = CSET_12_COL( CSchItem, individual_field_names );
|
||||
//
|
||||
// // This is fixed.
|
||||
// query = "SELECT * FROM Items";
|
||||
// columnSet = CSET_FULL( ... );
|
||||
|
||||
#define FOR_EACH_COLUMN_IN_SET( columnSet, iterName ) for( uint32 iterName = 0; iterName < (columnSet).GetColumnCount(); iterName++ )
|
||||
|
||||
#define CSET_EMPTY( schClass ) CColumnSet::Empty<schClass>()
|
||||
#define CSET_FULL( schClass ) CColumnSet::Full<schClass>()
|
||||
#define CSET_INSERTABLE( schClass ) CColumnSet::Insertable<schClass>()
|
||||
#define CSET_NONINSERTABLE( schClass ) CColumnSet::Noninsertable<schClass>()
|
||||
#define CSET_PK( schClass ) CColumnSet::PrimaryKey<schClass>()
|
||||
|
||||
#define CSET_1_COL( schClass, col1 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 )
|
||||
|
||||
#define CSET_2_COL( schClass, col1, col2 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 )
|
||||
|
||||
#define CSET_3_COL( schClass, col1, col2, col3 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 )
|
||||
|
||||
#define CSET_4_COL( schClass, col1, col2, col3, col4 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 )
|
||||
|
||||
#define CSET_5_COL( schClass, col1, col2, col3, col4, col5 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 )
|
||||
|
||||
#define CSET_6_COL( schClass, col1, col2, col3, col4, col5, col6 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col6 )
|
||||
|
||||
#define CSET_7_COL( schClass, col1, col2, col3, col4, col5, col6, col7 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col6 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col7 )
|
||||
|
||||
#define CSET_8_COL( schClass, col1, col2, col3, col4, col5, col6, col7, col8 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col6 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col7 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col8 )
|
||||
|
||||
#define CSET_9_COL( schClass, col1, col2, col3, col4, col5, col6, col7, col8, col9 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col6 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col7 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col8 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col9 )
|
||||
|
||||
#define CSET_10_COL( schClass, col1, col2, col3, col4, col5, col6, col7, col8, col9, col10 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col6 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col7 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col8 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col9 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col10 )
|
||||
|
||||
#define CSET_11_COL( schClass, col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col6 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col7 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col8 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col9 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col10 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col11 )
|
||||
|
||||
#define CSET_12_COL( schClass, col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12 ) \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col1 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col2 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col3 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col4 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col5 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col6 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col7 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col8 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col9 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col10 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col11 ) + \
|
||||
CColumnSet( GSchemaFull().GetSchema( schClass::k_iTable ).GetRecordInfo(), schClass::col12 )
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns an empty Column Set for a schema class
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename TSchClass >
|
||||
CColumnSet CColumnSet::Empty()
|
||||
{
|
||||
CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() );
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns a Column Set for a schema class which contains every field
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename TSchClass >
|
||||
CColumnSet CColumnSet::Full()
|
||||
{
|
||||
CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() );
|
||||
set.MakeFull();
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns a Column Set for a schema class which contains every
|
||||
// insertable field
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename TSchClass >
|
||||
CColumnSet CColumnSet::Insertable()
|
||||
{
|
||||
CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() );
|
||||
set.MakeInsertable();
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns a Column Set for a schema class which contains every
|
||||
// noninsertable field
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename TSchClass >
|
||||
CColumnSet CColumnSet::Noninsertable()
|
||||
{
|
||||
CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() );
|
||||
set.MakeNoninsertable();
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns a Column Set for a schema class which contains every
|
||||
// primary key field
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename TSchClass >
|
||||
CColumnSet CColumnSet::PrimaryKey()
|
||||
{
|
||||
CColumnSet set( GSchemaFull().GetSchema( TSchClass::k_iTable ).GetRecordInfo() );
|
||||
set.MakePrimaryKey();
|
||||
return set;
|
||||
}
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // COLUMNSET_H
|
||||
452
public/gcsdk/sqlaccess/record.h
Normal file
452
public/gcsdk/sqlaccess/record.h
Normal file
@@ -0,0 +1,452 @@
|
||||
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCRECORD_H
|
||||
#define GCRECORD_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "schema.h"
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
namespace GCSDK
|
||||
{
|
||||
#pragma pack( push, 1 )
|
||||
|
||||
class CRecordInfo;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// VarFieldBlockInfo_t
|
||||
// Tracks a block of memory used to hold all the variable-length
|
||||
// fields for a record.
|
||||
//-----------------------------------------------------------------------------
|
||||
struct VarFieldBlockInfo_t
|
||||
{
|
||||
union
|
||||
{
|
||||
// Take up 64-bits of space now even though
|
||||
// pointers are still 32 bits
|
||||
uint8* m_pubBlock;
|
||||
uint64 _unused;
|
||||
};
|
||||
uint32 m_cubBlock; // how much is in this block?
|
||||
uint32 m_cubBlockFree; // how much in this block is free?
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// VarField_t
|
||||
// Data format for a variable field entry in a DS record
|
||||
// For leaf code, is hidden inside a CVarField or CVarCharField
|
||||
//-----------------------------------------------------------------------------
|
||||
struct VarField_t
|
||||
{
|
||||
uint32 m_cubField; // Size of the field
|
||||
uint32 m_dubOffset; // Offset of the field within the block
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CVarField
|
||||
// Defines a class to encompass a variable-length field - opaque
|
||||
//-----------------------------------------------------------------------------
|
||||
class CVarField : private VarField_t
|
||||
{
|
||||
public:
|
||||
friend class CRecordVar;
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CVarCharField
|
||||
// Defines a class to encompass a variable-length string field - opaque
|
||||
//-----------------------------------------------------------------------------
|
||||
class CVarCharField : public CVarField
|
||||
{
|
||||
public:
|
||||
friend class CRecordVar;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CVarCharField
|
||||
// Defines a class to encompass a variable-length string field - opaque
|
||||
//-----------------------------------------------------------------------------
|
||||
class CVarBinaryField : public CVarField
|
||||
{
|
||||
public:
|
||||
friend class CRecordVar;
|
||||
};
|
||||
|
||||
|
||||
#pragma pack( pop )
|
||||
|
||||
// fix the size of this just to be safe
|
||||
#pragma pack( push, 4 )
|
||||
|
||||
class CSchema;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CRecordBase
|
||||
// Defines a class which arbitrates access to a fixed-length record
|
||||
//
|
||||
// This is used as a base class for the CSchTable wrapper classes emitted
|
||||
// by the schema compiler when the involved table has no variable length data.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CRecordBase
|
||||
{
|
||||
public:
|
||||
// These both allocate new space and COPY the record data into it
|
||||
CRecordBase( const CRecordBase &that );
|
||||
CRecordBase &operator=(const CRecordBase &that);
|
||||
|
||||
// Init from general memory
|
||||
int InitFromBytes( uint8 *pubRecord );
|
||||
|
||||
virtual ~CRecordBase();
|
||||
|
||||
// Use these when sending records over the wire
|
||||
uint32 CubSerialized();
|
||||
|
||||
virtual uint8 *PubRecordFixed();
|
||||
const uint8 *PubRecordFixed() const;
|
||||
uint32 CubRecordFixed() const;
|
||||
|
||||
virtual uint8 *PubRecordVarBlock();
|
||||
virtual const uint8 *PubRecordVarBlock() const;
|
||||
uint32 CubRecordVarBlock() const;
|
||||
bool BAssureRecordVarStorage( uint32 cVariableBytes );
|
||||
|
||||
// generic data accessors
|
||||
bool BGetField( int iField, uint8 **ppubData, uint32 *pcubField ) const;
|
||||
virtual bool BSetField( int iField, void * pvData, uint32 cubData );
|
||||
virtual void WipeField( int iField );
|
||||
|
||||
// data accessors
|
||||
const char * GetStringField( int iField, uint32 *pcubField );
|
||||
int GetInt( int iField );
|
||||
uint32 GetUint32( int iField );
|
||||
uint64 GetUint64( int iField );
|
||||
|
||||
// variable length data accessors
|
||||
// (not implemented by this class)
|
||||
virtual const char *ReadVarCharField( const CVarCharField &field ) const;
|
||||
virtual const uint8 *ReadVarDataField( const CVarField &field, uint32 *pcubField ) const;
|
||||
virtual bool SetVarCharField( CVarCharField &field, const char *pchString );
|
||||
virtual void SetVarDataField( CVarField &field, const void *pvData, uint32 cubData );
|
||||
|
||||
virtual const CSchema *GetPSchema() const
|
||||
{
|
||||
return const_cast<CRecordBase*>(this)->GetPSchema();
|
||||
}
|
||||
virtual CSchema *GetPSchema();
|
||||
const CRecordInfo *GetPRecordInfo() const;
|
||||
|
||||
// implemented by CSch-something derivatives
|
||||
virtual int GetITable() const = 0;
|
||||
|
||||
void RenderField( uint32 unColumn, int cchBuffer, char *pchBuffer ) const;
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
static void ValidateStatics( CValidator &validator, const char *pchName );
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
protected:
|
||||
// copies the contents of the record. The assignement operator uses this internally
|
||||
virtual void Copy( const CRecordBase & that );
|
||||
|
||||
CSchema *GetPSchemaImpl();
|
||||
void Cleanup();
|
||||
bool BSetField( int iField, void *pvData, uint32 cubData, bool *pbRealloced );
|
||||
|
||||
|
||||
// ctor for derived classes, CSch*
|
||||
CRecordBase( ) { }
|
||||
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CRecordVar
|
||||
// Defines a class which arbitrates access to a variable-length record
|
||||
//
|
||||
// This is used as a base class for the CSchTable wrapper classes emitted
|
||||
// by the schema compiler when the involved table *does* have variable-length data
|
||||
//-----------------------------------------------------------------------------
|
||||
class CRecordVar : public CRecordBase
|
||||
{
|
||||
public:
|
||||
CRecordVar( )
|
||||
{
|
||||
m_pSchema = NULL;
|
||||
m_nFlags = 0;
|
||||
}
|
||||
|
||||
virtual ~CRecordVar()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
virtual uint8* PubRecordFixed();
|
||||
const uint8 *PubRecordFixed() const;
|
||||
|
||||
virtual CSchema *GetPSchema()
|
||||
{
|
||||
return m_pSchema;
|
||||
}
|
||||
virtual const CSchema *GetPSchema() const
|
||||
{
|
||||
return m_pSchema;
|
||||
}
|
||||
|
||||
// Init from general memory
|
||||
int InitFromBytes( uint8 *pubRecord );
|
||||
|
||||
// generic data accessors
|
||||
virtual bool BSetField( int iField, void * pvData, uint32 cubData );
|
||||
virtual void WipeField( int iField );
|
||||
|
||||
// variable-length data accessors
|
||||
virtual const char *ReadVarCharField( const CVarCharField &field ) const;
|
||||
virtual const uint8 *ReadVarDataField( const CVarField &field, uint32 *pcubField ) const;
|
||||
virtual bool SetVarCharField( CVarCharField &field, const char *pchString );
|
||||
virtual void SetVarDataField( CVarField &field, const void *pvData, uint32 cubData );
|
||||
|
||||
// allocated fixed means we've got our own memory for the fixed record
|
||||
// allocated var block means we've allocated a block for the variable part of this record
|
||||
enum EFlags { k_EAllocatedFixed = 0x1, k_EAllocatedVarBlock = 0x2 };
|
||||
bool BFlagSet( int eFlag ) const;
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
protected:
|
||||
// copies the contents of the record. The assignement operator uses this internally
|
||||
virtual void Copy( const CRecordBase & that );
|
||||
|
||||
// initialization helper
|
||||
void DoInit()
|
||||
{
|
||||
m_pSchema = CRecordBase::GetPSchema();
|
||||
}
|
||||
|
||||
void SetFlag( int eFlag, bool bSet );
|
||||
|
||||
void Cleanup();
|
||||
CSchema *m_pSchema; // Corresponding Schema
|
||||
int m_nFlags; // Flags about the record memory allocations / location
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CRecordExternal
|
||||
// Defines a class which arbitrates access to a variable-length record
|
||||
//
|
||||
// This is used as an accessor for a polymorphic record. It can be used to
|
||||
// read CSchTable records when the type is unknown, manipulate stats records,
|
||||
// or touch data that isn't preallocated. Its use is relatively rare.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CRecordExternal : public CRecordVar
|
||||
{
|
||||
public:
|
||||
CRecordExternal()
|
||||
{
|
||||
m_pubRecordFixedExternal = NULL;
|
||||
m_nFlags = 0;
|
||||
}
|
||||
|
||||
virtual ~CRecordExternal()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
virtual uint8* PubRecordFixed();
|
||||
const uint8 *PubRecordFixed() const;
|
||||
|
||||
void DeSerialize( uint8 *pubData );
|
||||
|
||||
int GetITable() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Init( CSchema *pSchema );
|
||||
int Init( CSchema *pSchema, uint8 *pubRecord, bool bTakeOwnership );
|
||||
|
||||
// test helpers
|
||||
void InitRecordRandom( uint32 unPrimaryIndex );
|
||||
void SetFieldRandom( int iField );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
protected:
|
||||
// copies the contents of the record. The assignement operator uses this internally
|
||||
virtual void Copy( const CRecordBase & that );
|
||||
|
||||
void Cleanup();
|
||||
|
||||
void DoInit()
|
||||
{
|
||||
m_pSchema = CRecordBase::GetPSchema();
|
||||
}
|
||||
|
||||
uint8 *m_pubRecordFixedExternal; // If the fixed record is not a part of this object,
|
||||
// this points to where it is
|
||||
|
||||
CSchema *GetPSchemaImpl();
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Accessors for variable length character data.
|
||||
// These goofy little macros implement some syntax sugar
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define READ_VAR_CHAR_FIELD( record, field )\
|
||||
(record).ReadVarCharField( (record).field )
|
||||
|
||||
#define WRITE_VAR_CHAR_FIELD( record, field, text )\
|
||||
(record).SetVarCharField( (record).field, text )
|
||||
|
||||
#pragma pack( pop )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Template classes that get a LessFunc that sorts CRecordBases by a field
|
||||
// within them
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T, int I, typename F>
|
||||
class CDefSchOps
|
||||
{
|
||||
public:
|
||||
static bool LessFunc( const T &lhs, const T &rhs )
|
||||
{
|
||||
// Check that the field number is valid
|
||||
COMPILE_TIME_ASSERT( I >= 0 && I < T::k_iFieldMax );
|
||||
|
||||
// Check to make sure this is a fixed field
|
||||
const Field_t &fieldInfo = lhs.GetPSchema()->GetField( I );
|
||||
Assert( !fieldInfo.BIsStringType() && !fieldInfo.BIsVariableLength() );
|
||||
if ( fieldInfo.BIsStringType() || fieldInfo.BIsVariableLength() )
|
||||
return false;
|
||||
|
||||
// Read the data and make sure the sizes are correct for the field type we expect
|
||||
uint8 *pubLhs;
|
||||
uint8 *pubRhs;
|
||||
bool bRet;
|
||||
uint32 cubRead;
|
||||
|
||||
bRet = lhs.BGetField( I, &pubLhs, &cubRead );
|
||||
Assert( bRet && cubRead == sizeof( F ) );
|
||||
if ( !bRet || cubRead != sizeof( F ) )
|
||||
return false;
|
||||
|
||||
bRet = rhs.BGetField( I, &pubRhs, &cubRead );
|
||||
Assert( bRet && cubRead == sizeof( F ) );
|
||||
if ( !bRet || cubRead != sizeof( F ) )
|
||||
return false;
|
||||
|
||||
// Finally do the comparison
|
||||
return ( *( (F *)pubLhs ) ) < ( *( (F *)pubRhs ) );
|
||||
}
|
||||
|
||||
static bool LessFuncCtx( const T &lhs, const T &rhs, void *pCtx )
|
||||
{
|
||||
return LessFunc( lhs, rhs );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define DefSchLessFunc( RecordType, FieldIndex, FieldType ) CDefSchOps<RecordType, FieldIndex, FieldType>::LessFunc
|
||||
#define DefSchLessFuncCtx( RecordType, FieldIndex, FieldType ) CDefSchOps<RecordType, FieldIndex, FieldType>::LessFuncCtx
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Specializations for string fields
|
||||
//-----------------------------------------------------------------------------
|
||||
template <class T, int I>
|
||||
class CDefSchOps<T, I, char *>
|
||||
{
|
||||
public:
|
||||
static bool LessFunc( const T &lhs, const T &rhs )
|
||||
{
|
||||
// Check that the field number is valid
|
||||
COMPILE_TIME_ASSERT( I >= 0 && I < T::k_iFieldMax );
|
||||
|
||||
// Check to make sure this is indeed a string field
|
||||
Field_t &fieldInfo = lhs.GetPSchema()->GetField( I );
|
||||
Assert( fieldInfo.BIsStringType() );
|
||||
if ( !fieldInfo.BIsStringType() )
|
||||
return false;
|
||||
|
||||
// Read the data
|
||||
uint32 cubRead;
|
||||
const char *pchLhs = lhs.GetStringField( I, &cubRead );
|
||||
const char *pchRhs = rhs.GetStringField( I, &cubRead );
|
||||
|
||||
// Finally do the comparison
|
||||
return CDefOps<const char *>::LessFunc( lhs, rhs );
|
||||
}
|
||||
|
||||
static bool LessFuncCtx( const T &lhs, const T &rhs, void *pCtx )
|
||||
{
|
||||
return LessFunc( lhs, rhs );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class T, int I>
|
||||
class CDefSchOps<T, I, const char *>
|
||||
{
|
||||
public:
|
||||
static bool LessFunc( const T &lhs, const T &rhs )
|
||||
{
|
||||
return CDefSchOps<T, I, char *>::LessFunc( lhs, rhs );
|
||||
}
|
||||
|
||||
static bool LessFuncCtx( const T &lhs, const T &rhs, void *pCtx )
|
||||
{
|
||||
return LessFunc( lhs, rhs );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Provide a convenient object to pass around to represent a type
|
||||
// of record
|
||||
//-----------------------------------------------------------------------------
|
||||
class CRecordType
|
||||
{
|
||||
public:
|
||||
virtual int GetITable() const = 0;
|
||||
virtual CRecordBase *Create() const = 0;
|
||||
|
||||
CSchema *GetSchema() const;
|
||||
CRecordInfo *GetRecordInfo() const;
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
template <typename TRecord>
|
||||
class CRecordTypeConcrete: public CRecordType
|
||||
{
|
||||
public:
|
||||
virtual int GetITable() const { return TRecord::k_iTable; }
|
||||
virtual CRecordBase *Create() const { return new TRecord(); }
|
||||
};
|
||||
|
||||
#define RTYPE( recordClass ) CRecordTypeConcrete<recordClass>()
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // GCRECORD_H
|
||||
171
public/gcsdk/sqlaccess/recordinfo.h
Normal file
171
public/gcsdk/sqlaccess/recordinfo.h
Normal file
@@ -0,0 +1,171 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCRECORDINFO_H
|
||||
#define GCRECORDINFO_H
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
typedef CUtlMap<const char *,int> CMapIColumnInfo;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Information about a column in a record (table or result set)
|
||||
class CColumnInfo
|
||||
{
|
||||
public:
|
||||
CColumnInfo();
|
||||
~CColumnInfo() { }
|
||||
|
||||
void Set( const char *pchName, int nSQLColumn, EGCSQLType eGCSQLType, int cubFixedSize, int nColFlags, int cubMaxSize );
|
||||
const char *GetName() const { return m_rgchName; }
|
||||
int GetSQLColumn() const { return m_nSQLColumn; }
|
||||
EGCSQLType GetType() const { return m_eType; }
|
||||
int GetFixedSize() const { return m_cubFixedSize; }
|
||||
int GetMaxSize() const { return m_cchMaxSize; }
|
||||
int GetChecksum() const { Assert( m_bHaveChecksum ); return m_nChecksum; }
|
||||
bool BIsVariableLength() const;
|
||||
int GetColFlags() const { return m_nColFlags; }
|
||||
void GetColFlagDescription( char* pstrOut, int cubOutLength ) const;
|
||||
int GetConstraintColFlags() { return m_nColFlags & k_nColFlagAllConstraints; }
|
||||
void SetColFlagBits( int nColFlag );
|
||||
bool BIsIndexed() const { return 0 != ( m_nColFlags & k_nColFlagIndexed ); }
|
||||
bool BIsClustered() const { return 0 != ( m_nColFlags & k_nColFlagClustered ); }
|
||||
bool BIsUnique() const { return 0 != ( m_nColFlags & k_nColFlagUnique ); }
|
||||
bool BIsAutoIncrement() const { return 0 != ( m_nColFlags & k_nColFlagAutoIncrement ); }
|
||||
bool BIsPrimaryKey() const { return 0 != ( m_nColFlags & k_nColFlagPrimaryKey ); }
|
||||
bool BIsExplicitlyIndexed() const { return BIsIndexed() && !( BIsPrimaryKey() || BIsUnique() ); }
|
||||
bool BIsExplicitlyUnique() const { return BIsUnique() && !BIsPrimaryKey(); }
|
||||
bool BIsInsertable() const { return !BIsAutoIncrement(); }
|
||||
void CalculateChecksum();
|
||||
void ValidateColFlags() const;
|
||||
bool operator==( const CColumnInfo& refOther ) const;
|
||||
bool operator!=( const CColumnInfo& refOther ) const
|
||||
{
|
||||
return ! operator==( refOther );
|
||||
}
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName );
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
private:
|
||||
CColumnInfo( CColumnInfo& ); // no copy constructor, disable default copy constructor
|
||||
CColumnInfo& operator = ( CColumnInfo& ); // no assignment operator, disable default assignment operator
|
||||
char m_rgchName[k_cSQLObjectNameMax+1];
|
||||
|
||||
EGCSQLType m_eType; // GC-based enum data type of this column
|
||||
int m_nColFlags; // flags for this column
|
||||
int m_nSQLColumn; // column # in SQL database to bind to, starts at 1.
|
||||
int m_cubFixedSize; // if fixed size, the fixed size in bytes; else 0
|
||||
int m_cchMaxSize; // if variable size, the maximum size; else 0
|
||||
int m_nChecksum; // checksum of this column info for quick comparisons
|
||||
bool m_bHaveChecksum; // have we calculated a checksum yet?
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Information about a record (table or result set)
|
||||
class CRecordInfo : public CRefCount
|
||||
{
|
||||
public:
|
||||
CRecordInfo();
|
||||
|
||||
void InitFromDSSchema( CSchema *pSchema );
|
||||
|
||||
void SetName( const char *pchName );
|
||||
const char *GetName() const { return m_rgchName; }
|
||||
void AddColumn( const char *pchName, int nSQLColumn, EGCSQLType eGCSQLType, int cubFixedSize, int nColFlags, int cubMaxSize );
|
||||
void SetAllColumnsAdded() { m_bAllColumnsAdded = true; }
|
||||
void PrepareForUse();
|
||||
int GetFixedSize() const { return m_cubFixedSize; }
|
||||
int GetNumColumns() const { return m_VecColumnInfo.Count(); }
|
||||
const CColumnInfo &GetColumnInfo( uint32 unColumn ) const { return m_VecColumnInfo[unColumn]; }
|
||||
CColumnInfo &GetColumnInfo( uint32 unColumn ) { return m_VecColumnInfo[unColumn]; }
|
||||
bool BFindColumnByName( const char *pchName, int *piColumn );
|
||||
bool BPreparedForUse() const { return m_bPreparedForUse; }
|
||||
void EnsureCapacity( int cColumns ) { m_VecColumnInfo.EnsureCapacity( cColumns ); }
|
||||
int GetChecksum();
|
||||
ESchemaCatalog GetESchemaCatalog() const { return m_eSchemaCatalog; }
|
||||
void SetESchemaCatalog( ESchemaCatalog e ) { m_eSchemaCatalog = e; }
|
||||
bool EqualTo( CRecordInfo* pOther );
|
||||
bool CompareIndexLists( CRecordInfo *pOther );
|
||||
bool CompareFKs( CRecordInfo *pOther );
|
||||
bool CompareFTSIndexLists( CRecordInfo *pOther ) const;
|
||||
EPrimaryKeyType GetPrimaryKeyType() const { return m_nHasPrimaryKey; }
|
||||
bool BHasPrimaryKey() { return GetPrimaryKeyType() != k_EPrimaryKeyTypeNone; }
|
||||
const FieldSet_t& GetPKFields() { Assert( BHasPrimaryKey()); return GetIndexFields( )[ m_iPKIndex ]; }
|
||||
const CUtlVector<FieldSet_t>& GetIndexFields() const { return m_VecIndexes; }
|
||||
int GetIndexFieldCount() const { return m_VecIndexes.Count(); }
|
||||
int GetPKIndex() const { return m_iPKIndex; }
|
||||
int AddIndex( const FieldSet_t& fieldSet );
|
||||
void GetIndexFieldList( CFmtStr1024 *pstr, int nIndents ) const;
|
||||
int GetTableID() const { return m_nTableID; }
|
||||
void SetTableID( int nTableID ) { m_nTableID = nTableID; }
|
||||
bool BHasIdentity() const;
|
||||
|
||||
// full-text index
|
||||
CUtlVector<int> & GetFTSFields() { return m_vecFTSFields; }
|
||||
bool BHasFTSIndex() const { return m_vecFTSFields.Count() > 0; }
|
||||
void AddFTSFields( CUtlVector< int > &refVecFields );
|
||||
int GetFullTextCatalogIndex() { return m_nFullTextCatalogIndex; }
|
||||
|
||||
// foreign keys
|
||||
void AddFK( const FKData_t &fkData );
|
||||
void GetFKListString( CFmtStr1024 *pstr, int nIndents );
|
||||
int GetFKCount();
|
||||
FKData_t &GetFKData( int iIndex );
|
||||
|
||||
|
||||
static CRecordInfo *Alloc();
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
static void ValidateStatics( CValidator &validator, const char *pchName );
|
||||
void Validate( CValidator &validator, const char *pchName );
|
||||
#endif //DBGFLAG_VALIDATE
|
||||
|
||||
|
||||
// note: destructor is private. This is a ref-counted object, private destructor ensures callers can't accidentally delete
|
||||
// directly, or declare on stack
|
||||
virtual ~CRecordInfo() { }
|
||||
|
||||
private:
|
||||
virtual void DestroyThis();
|
||||
void CalculateChecksum();
|
||||
void BuildColumnNameIndex();
|
||||
|
||||
char m_rgchName[k_cSQLObjectNameMax+1];
|
||||
int m_nTableID; // Object_ID if this table in SQL Server
|
||||
CUtlVector<CColumnInfo> m_VecColumnInfo; // Vector of columns in this record
|
||||
CMapIColumnInfo m_MapIColumnInfo; // Map of name->column index for quick lookup by name
|
||||
EPrimaryKeyType m_nHasPrimaryKey; // Does this table contain a column that is a primary key?
|
||||
int m_iPKIndex; // index info m_VecIndexes of our PK index; -1 if no PK
|
||||
CUtlVector<FieldSet_t> m_VecIndexes; // vector of all fields in all indexes
|
||||
int m_cubFixedSize; // Sum of data sizes for all fixed size columns
|
||||
bool m_bAllColumnsAdded; // Have all columns been added
|
||||
bool m_bPreparedForUse; // Have we finished being initialized?
|
||||
bool m_bHaveColumnNameIndex; // Have we created a column name index? (Only generated if someone asks.)
|
||||
bool m_bHaveChecksum; // Have we generated a checksum? (Only generated if someone asks.)
|
||||
int m_nChecksum; // checksum of this record info for quick comparisons - includes all columns
|
||||
ESchemaCatalog m_eSchemaCatalog; // what catalog owns this object?
|
||||
CUtlVector< int > m_vecFTSFields; // which fields have FTS indexing?
|
||||
int m_nFullTextCatalogIndex; // index of catalog for FTS index, if we get one
|
||||
CUtlVector<FKData_t> m_VecFKData; // vector of all FK relationships defined on this table
|
||||
|
||||
CThreadMutex m_Mutex;
|
||||
static CThreadSafeClassMemoryPool<CRecordInfo> sm_MemPoolRecordInfo;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// validation tracking
|
||||
static CUtlRBTree<CRecordInfo *, int > sm_mapPMemPoolRecordInfo;
|
||||
static CThreadMutex sm_mutexMemPoolRecordInfo;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
int __cdecl CompareColumnInfo( const CColumnInfo *pColumnInfoLeft, const CColumnInfo *pColumnInfoRight );
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // GCRECORDINFO_H
|
||||
591
public/gcsdk/sqlaccess/schema.h
Normal file
591
public/gcsdk/sqlaccess/schema.h
Normal file
@@ -0,0 +1,591 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSCHEMA_H
|
||||
#define GCSCHEMA_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
const int k_nColFlagIndexed = 0x0001; // this column is indexed.
|
||||
const int k_nColFlagUnique = 0x0002; // this column has a uniqueness constraint - creates implicit index
|
||||
const int k_nColFlagPrimaryKey = 0x0004; // this column has a primary key constraint - creates implicit uniqueness constraint & implicit index
|
||||
const int k_nColFlagAutoIncrement = 0x0008; // this column can have it's values created implicitly by the sql counter
|
||||
const int k_nColFlagClustered = 0x0010; // this column is clustered
|
||||
|
||||
const int k_nColFlagAllConstraints = k_nColFlagUnique | k_nColFlagPrimaryKey;
|
||||
|
||||
class CRecordInfo;
|
||||
struct VarFieldBlockInfo_t;
|
||||
struct VarFieldBlockInfo_t;
|
||||
struct Field_t;
|
||||
struct VarField_t;
|
||||
|
||||
// Function type for altering field types when converting schemas
|
||||
typedef void (* PfnAlterField_t )( void *pvDest, const void *pvSrc );
|
||||
|
||||
// EPrimaryKeyType
|
||||
// This shows if a table has a primary key and, if so, if it has multiple columns
|
||||
// or not.
|
||||
enum EPrimaryKeyType
|
||||
{
|
||||
k_EPrimaryKeyTypeNone = 0, // none at all
|
||||
k_EPrimaryKeyTypeSingle, // single-column primary key
|
||||
k_EPrimaryKeyTypeMulti, // multi-column primary key
|
||||
};
|
||||
|
||||
|
||||
// EWipePolicy
|
||||
// This tells us if a table is supposed to be wiped before all tests,
|
||||
// before all tests except stress tests, or not to be wiped before tests.
|
||||
enum EWipePolicy
|
||||
{
|
||||
k_EWipePolicyPreserveAlways = 0, // don't wipe table
|
||||
k_EWipePolicyPreserveForStress = 1, // preserve for stress tests, wipe before regular tests
|
||||
k_EWipePolicyWipeForAllTests = 2, // wipe table prior to all tests
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Field
|
||||
// This defines the schema of a single field in one of our tables.
|
||||
//-----------------------------------------------------------------------------
|
||||
struct Field_t
|
||||
{
|
||||
bool BGetIntData( uint8 *pubRecord, uint64 *pulRet ) const;
|
||||
bool BGetFloatData( uint8 *pubRecord, float *fRet ) const;
|
||||
bool SetIntData( uint8 *pubRecord, uint64 ulValue ) const;
|
||||
// Not all fields are updated wholly at a time
|
||||
int CubFieldUpdateSize() const;
|
||||
// Handy helpers - complex fields like "rolling unique" are
|
||||
// still binary
|
||||
bool BIsStringType() const;
|
||||
|
||||
bool BIsVariableLength() const;
|
||||
|
||||
// Members that get serialized
|
||||
EGCSQLType m_EType; // Field type
|
||||
uint32 m_cubLength; // Length of the field in bytes
|
||||
uint32 m_cchMaxLength; // maximum length of the field in characters
|
||||
char m_rgchName[k_cSQLObjectNameMax]; // Human-readable name of this field
|
||||
char m_rgchSQLName[k_cSQLObjectNameMax]; // SQL name of this field
|
||||
|
||||
// Members that don't get serialized
|
||||
uint32 m_nColFlags; // k_nColFlag* bits for this field
|
||||
uint32 m_dubOffset; // Offset of this field from beginning of record
|
||||
};
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Information about a foreign key relationship defined on a table
|
||||
struct FKColumnRelation_t
|
||||
{
|
||||
char m_rgchCol[k_cSQLObjectNameMax+1];
|
||||
char m_rgchParentCol[k_cSQLObjectNameMax+1];
|
||||
|
||||
FKColumnRelation_t()
|
||||
{
|
||||
Q_memset( m_rgchCol, 0, Q_ARRAYSIZE( m_rgchCol ) );
|
||||
Q_memset( m_rgchParentCol, 0, Q_ARRAYSIZE( m_rgchParentCol ) );
|
||||
}
|
||||
|
||||
bool operator==( const FKColumnRelation_t &other ) const
|
||||
{
|
||||
if ( Q_stricmp( m_rgchCol, other.m_rgchCol ) )
|
||||
return false;
|
||||
if ( Q_stricmp( m_rgchParentCol, other.m_rgchParentCol ) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct FKData_t
|
||||
{
|
||||
char m_rgchName[k_cSQLObjectNameMax+1];
|
||||
char m_rgchParentTableName[k_cSQLObjectNameMax+1];
|
||||
CCopyableUtlVector<FKColumnRelation_t> m_VecColumnRelations;
|
||||
EForeignKeyAction m_eOnDeleteAction;
|
||||
EForeignKeyAction m_eOnUpdateAction;
|
||||
|
||||
FKData_t()
|
||||
{
|
||||
Q_memset( m_rgchName, 0, Q_ARRAYSIZE( m_rgchName ) );
|
||||
Q_memset( m_rgchParentTableName, 0, Q_ARRAYSIZE( m_rgchParentTableName ) );
|
||||
m_eOnDeleteAction = k_EForeignKeyActionNoAction;
|
||||
m_eOnUpdateAction = k_EForeignKeyActionNoAction;
|
||||
}
|
||||
|
||||
bool operator==( const FKData_t &other ) const
|
||||
{
|
||||
if ( Q_stricmp( m_rgchName, other.m_rgchName ) )
|
||||
return false;
|
||||
|
||||
if ( Q_stricmp( m_rgchParentTableName, other.m_rgchParentTableName ) )
|
||||
return false;
|
||||
|
||||
if ( m_eOnDeleteAction != other.m_eOnDeleteAction || m_eOnUpdateAction != other.m_eOnUpdateAction )
|
||||
return false;
|
||||
|
||||
FOR_EACH_VEC( m_VecColumnRelations, i )
|
||||
{
|
||||
bool bFoundInOther = false;
|
||||
const FKColumnRelation_t &cols = m_VecColumnRelations[i];
|
||||
|
||||
FOR_EACH_VEC( other.m_VecColumnRelations, j )
|
||||
{
|
||||
const FKColumnRelation_t &colsOther = other.m_VecColumnRelations[j];
|
||||
if ( cols == colsOther )
|
||||
{
|
||||
bFoundInOther = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !bFoundInOther )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
// Validate our internal structures
|
||||
void Validate( CValidator &validator, const char *pchName )
|
||||
{
|
||||
VALIDATE_SCOPE();
|
||||
ValidateObj( m_VecColumnRelations );
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
#pragma pack( push, 1 )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SerSchema
|
||||
// Defines the binary serialization format for a schema.
|
||||
//-----------------------------------------------------------------------------
|
||||
struct SerSchema_t
|
||||
{
|
||||
uint32 m_cub; // Size of the whole schema (including header and fields)
|
||||
int32 m_iTable; // Our table's iTable
|
||||
char m_rgchName[k_cSQLObjectNameMax]; // Human-readable name of this table
|
||||
int16 m_cField; // # of fields in the schema (int16 for backward-compatibility reasons)
|
||||
int16 m_ETableGroup; // Our table's TableGroup (int16 for backward-compatibility reasons) - OBSOLETE
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SerField
|
||||
// Defines the binary serialization format for a field in a schema
|
||||
// Note that certain fields are missing from this because we only use serialized
|
||||
// schemas for schema mapping. Fields that only affect runtime behavior (like
|
||||
// indexing) are always defined by the intrinsic schema.
|
||||
//-----------------------------------------------------------------------------
|
||||
struct SerField_t
|
||||
{
|
||||
int32 m_EType; // Field type
|
||||
uint32 m_cubLength; // Length of field data in bytes
|
||||
// For rolling fields, high 16 bits are the
|
||||
// size of each element
|
||||
|
||||
char m_rgchName[k_cSQLObjectNameMax];// Human-readable name of this field
|
||||
char m_rgchSQLName[k_cSQLObjectNameMax]; // SQL name of this field
|
||||
};
|
||||
|
||||
#pragma pack( pop )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Schema conversion instructions
|
||||
// These specify various operations that can be performed when converting
|
||||
// from one Schema to another.
|
||||
//-----------------------------------------------------------------------------
|
||||
struct DeleteField_t
|
||||
{
|
||||
char m_rgchFieldName[k_cSQLObjectNameMax]; // Name of the field to delete
|
||||
};
|
||||
|
||||
struct RenameField_t
|
||||
{
|
||||
char m_rgchFieldNameOld[k_cSQLObjectNameMax]; // Rename a field with this name
|
||||
int m_iFieldDst; // to this field
|
||||
};
|
||||
|
||||
struct AlterField_t
|
||||
{
|
||||
char m_rgchFieldNameOld[k_cSQLObjectNameMax]; // Name of field in the old schema
|
||||
int m_iFieldDst; // iField of it in the new
|
||||
PfnAlterField_t m_pfnAlterFunc; // Function to translate the data
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FieldSet_t describes a collection of fields in an index, as well as
|
||||
// attributes of the index itself
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class FieldSet_t
|
||||
{
|
||||
public:
|
||||
FieldSet_t( bool bUnique, bool bClustered, const CUtlVector<int>& vecFields, const char* pstrIndexName )
|
||||
: m_bClustered( bClustered ), m_bUnique( bUnique )
|
||||
{
|
||||
m_VecFields = vecFields;
|
||||
|
||||
// zero means to use the server default
|
||||
m_nFillFactor = 0;
|
||||
|
||||
// null name is allowed for primary keys
|
||||
if ( pstrIndexName != NULL )
|
||||
Q_strncpy( m_szIndexName, pstrIndexName, Q_ARRAYSIZE( m_szIndexName ) );
|
||||
else
|
||||
m_szIndexName[0] = 0;
|
||||
}
|
||||
|
||||
FieldSet_t( )
|
||||
{
|
||||
}
|
||||
|
||||
~FieldSet_t( )
|
||||
{
|
||||
}
|
||||
|
||||
FieldSet_t( const FieldSet_t &refOther )
|
||||
{
|
||||
DoAssignment( refOther );
|
||||
}
|
||||
|
||||
FieldSet_t& operator=( const FieldSet_t &refOther )
|
||||
{
|
||||
DoAssignment( refOther );
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DoAssignment( const FieldSet_t &refOther )
|
||||
{
|
||||
m_VecFields = refOther.m_VecFields;
|
||||
m_VecIncluded = refOther.m_VecIncluded;
|
||||
m_bClustered = refOther.m_bClustered;
|
||||
m_bUnique = refOther.m_bUnique;
|
||||
m_nFillFactor = refOther.m_nFillFactor;
|
||||
Q_strncpy( m_szIndexName, refOther.m_szIndexName, Q_ARRAYSIZE( m_szIndexName ) );
|
||||
|
||||
}
|
||||
|
||||
// get count of fields in this index
|
||||
int GetCount() const
|
||||
{
|
||||
return m_VecFields.Count();
|
||||
}
|
||||
|
||||
// get count of included fields in this index
|
||||
int GetIncludedCount() const
|
||||
{
|
||||
return m_VecIncluded.Count();
|
||||
}
|
||||
|
||||
void AddIncludedColumn( int nIndex )
|
||||
{
|
||||
m_VecIncluded.AddToTail( nIndex );
|
||||
}
|
||||
|
||||
void AddIncludedColumns( const CUtlVector<int> &refVec )
|
||||
{
|
||||
m_VecIncluded.AddVectorToTail( refVec );
|
||||
}
|
||||
|
||||
// get a particular field ID
|
||||
// the returned index is into the VecFields of the associated schema
|
||||
int GetField( int nIndex ) const
|
||||
{
|
||||
return m_VecFields[ nIndex ];
|
||||
}
|
||||
|
||||
int GetIncludedField( int nIndex ) const
|
||||
{
|
||||
return m_VecIncluded[ nIndex ];
|
||||
}
|
||||
|
||||
// is this index clustered?
|
||||
bool IsClustered() const
|
||||
{
|
||||
return m_bClustered;
|
||||
}
|
||||
|
||||
// is this index unique?
|
||||
bool IsUnique() const
|
||||
{
|
||||
return m_bUnique;
|
||||
}
|
||||
|
||||
void SetClustered( bool bIsClustered )
|
||||
{
|
||||
m_bClustered = bIsClustered;
|
||||
}
|
||||
|
||||
void SetFillFactor( int nFactor )
|
||||
{
|
||||
Assert( nFactor >= 0 && nFactor <= 100 );
|
||||
m_nFillFactor = nFactor;
|
||||
}
|
||||
|
||||
int GetFillFactor( ) const
|
||||
{
|
||||
return m_nFillFactor;
|
||||
}
|
||||
|
||||
const char* GetIndexName() const
|
||||
{
|
||||
return m_szIndexName;
|
||||
}
|
||||
|
||||
// determine if this fieldset is equal to the other one
|
||||
static bool CompareFieldSets( const FieldSet_t& refThis, CRecordInfo* pRecordInfoThis,
|
||||
const FieldSet_t& refOther, CRecordInfo* pRecordInfoOther );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
// Validate our internal structures
|
||||
void Validate( CValidator &validator, const char *pchName )
|
||||
{
|
||||
VALIDATE_SCOPE();
|
||||
m_VecFields.Validate( validator, "m_VecFields" );
|
||||
m_VecIncluded.Validate( validator, "m_VecIncluded" );
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
CUtlVector<int> m_VecFields; // ids of fields; indexes into m_VecFields of CSchema for a table
|
||||
CUtlVector<int> m_VecIncluded; // ids of included fields
|
||||
int m_nFillFactor; // fill factor for the index; zero means to use the server's default
|
||||
char m_szIndexName[k_cSQLObjectNameMax]; // name of this index
|
||||
bool m_bClustered:1; // is this index clustered?
|
||||
bool m_bUnique:1; // is this index unique?
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
enum ESchemaCatalog
|
||||
{
|
||||
k_ESchemaCatalogInvalid = -1,
|
||||
k_ESchemaCatalogMain = 0, // main GC catalog
|
||||
k_ESchemaCatalogOGS = 4, // operational game stats
|
||||
};
|
||||
|
||||
extern const char* PchNameFromESchemaCatalog( ESchemaCatalog e );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CSchema
|
||||
// This defines the schema for a single table. The schema essentially defines
|
||||
// what's in the table (ie, field 0 is a 32 char string called "Name", etc.)
|
||||
// The schema is in charge of manipulating individual records within the table.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class CSchemaFull;
|
||||
|
||||
class CSchema
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructors & destructors
|
||||
CSchema();
|
||||
~CSchema();
|
||||
|
||||
// Recalculates field offsets and maximum record size.
|
||||
// Must be called after changing schema.
|
||||
void CalcOffsets();
|
||||
|
||||
// called to make final calculations when all fields/indexes/etc have been added and the schema is ready to be used
|
||||
void PrepareForUse();
|
||||
|
||||
// Sizing information
|
||||
uint32 CubSerialSchema() const { return( sizeof( SerSchema_t ) + m_VecField.Count() * sizeof( SerField_t ) ); }
|
||||
|
||||
// Size of total fixed-length portion of record
|
||||
uint32 CubRecordFixed() const { return( m_cubRecord ); }
|
||||
|
||||
// Size of the total variable-length portion of record (zero if no var-length fields)
|
||||
uint32 CubRecordVariable( const void *pvRecord ) const;
|
||||
|
||||
// Does this record have variable-length fields?
|
||||
bool BHasVariableFields() const { return m_bHasVarFields; }
|
||||
|
||||
VarFieldBlockInfo_t* PVarFieldBlockInfoFromRecord( const void *pvRecord ) const;
|
||||
|
||||
// Access field data - fixed or variable (may return NULL/0 for empty var field)
|
||||
bool BGetFieldData( const void *pvRecord, int iField, uint8 **ppubField, uint32 *pcubField ) const;
|
||||
bool BSetFieldData( void *pvRecord, int iField, uint8 *pubField, uint32 cubField, bool *pbVarBlockRealloced );
|
||||
|
||||
bool BGetVarField( const void *pvRecord, const VarField_t *pVarField, uint8 **ppubField, uint32 *pcubField ) const;
|
||||
bool BSetVarField( void *pvRecord, VarField_t *pVarField, const void *pvData, uint32 cubData, bool *pbRealloced, bool bFreeOnRealloc );
|
||||
|
||||
// Adjust var-block pointer, if present, to point just after the fixed part of the record
|
||||
void FixupDeserializedRecord( void *pvRecord );
|
||||
|
||||
// Render a record in text format
|
||||
void RenderRecord( uint8 *pubRecord );
|
||||
void RenderField( uint8 *pubRecord, int iField, int cchBuffer, char *pchBuffer );
|
||||
|
||||
// Accessors
|
||||
void SetITable( int iTable ) { m_iTable = iTable; }
|
||||
int GetITable() const { return m_iTable; }
|
||||
int GetCField() const { return m_VecField.Count(); }
|
||||
void SetReportingInterval( int nInterval ) { m_nReportingInterval = nInterval; }
|
||||
int GetReportingInterval( ) const { return m_nReportingInterval; }
|
||||
Field_t &GetField( int iField ) { return m_VecField[iField]; }
|
||||
const Field_t &GetField( int iField ) const { return m_VecField[iField]; }
|
||||
VarField_t *GetPVarField( void *pvRecord, int iField ) { return ( VarField_t * )( ( uint8 * ) pvRecord + m_VecField[iField].m_dubOffset ); }
|
||||
void SetName( const char *pchName ) { Q_strncpy( m_rgchName, pchName, sizeof( m_rgchName ) ); }
|
||||
const char *GetPchName() const { return m_rgchName; }
|
||||
const FieldSet_t& GetPKFields() { Assert( m_iPKIndex != -1 ); return GetIndexes()[m_iPKIndex]; }
|
||||
int GetPKIndex() const { return m_iPKIndex; }
|
||||
const CUtlVector<FieldSet_t>& GetIndexes() { return m_VecIndexes; }
|
||||
const CUtlVector<int>& GetFTSColumns() { return m_VecFullTextIndexes; }
|
||||
int GetFTSIndexCatalog() const { return m_nFullTextIndexCatalog; }
|
||||
|
||||
ESchemaCatalog GetESchemaCatalog() const { return m_eSchemaCatalog; }
|
||||
void SetESchemaCatalog( ESchemaCatalog eSchemaCatalog ) { m_eSchemaCatalog = eSchemaCatalog; }
|
||||
|
||||
// If cRecordMax is non-zero, this is a rolling table that only
|
||||
// holds on to cRecordMax records at most.
|
||||
void SetCRecordMax( int cRecordMax ) { m_cRecordMax = cRecordMax; }
|
||||
int GetCRecordMax() const { return m_cRecordMax; }
|
||||
|
||||
// Is this table for TESTs only?
|
||||
void SetBTestTable( bool bTestTable ) { m_bTestTable = bTestTable; }
|
||||
bool GetBTestTable() const { return m_bTestTable; }
|
||||
|
||||
// Randomly init a record or field to random values
|
||||
void InitRecordRandom( uint8 *pubRecord, uint32 unPrimaryIndex, bool *pbVarBlockRealloced, bool bFreeVarBlockOnRealloc );
|
||||
void SetFieldRandom( uint8 *pubRecord, int iField, bool *pbVarBlockRealloced, bool bFreeVarBlockOnRealloc );
|
||||
|
||||
// Checksum the schema
|
||||
uint32 CalcChecksum();
|
||||
|
||||
// pre-allocate space in the field array
|
||||
void EnsureFieldCount( int cFields )
|
||||
{
|
||||
m_VecField.EnsureCapacity( cFields );
|
||||
}
|
||||
|
||||
// This adds a field from our intrinsic schema to us
|
||||
void AddField( char *pchName, char *pchSQLName, EGCSQLType eType, uint32 cubSize, int cchMaxLength );
|
||||
void AddIntField( char *pchName, char *pchSQLName, EGCSQLType eType, int cubSize );
|
||||
|
||||
// We want to make a particular field the primary key
|
||||
int PrimaryKey( bool bClustered, int nFillFactor, const char *pchName );
|
||||
|
||||
// we want to make a particular list of fields the primary key
|
||||
int PrimaryKeys( bool bClustered, int nFillFactor, const char *pchNames );
|
||||
|
||||
// We want to index a particular field by name
|
||||
int IndexField( const char *pchName, const char *pchIndexName );
|
||||
|
||||
// We want to index a particular list of fields in a group
|
||||
int IndexFields( const char *pchIndexName, const char *pchNames );
|
||||
|
||||
// We want a certain index to additionally include a list of fields
|
||||
void AddIncludedFields( const char *pchIndexName, const char *pchNames );
|
||||
|
||||
// We want to unique index a particular list of fields in a group
|
||||
int UniqueFields( const char *pchIndexName, const char *pchNames );
|
||||
|
||||
// add a full-text index to the given column
|
||||
void AddFullTextIndex( CSchemaFull *pSchemaFull, const char *pchCatalogName, const char *pchColumnName );
|
||||
|
||||
// We want to index a particular field by field number
|
||||
// (field number is an index into the m_VecField array)
|
||||
int AddIndexToFieldNumber( int iField, const char *pchIndexName, bool bClustered );
|
||||
|
||||
// We want to index a particular set of fields
|
||||
// pchNames includes the names, separated by commas, of each field
|
||||
int AddIndexToFieldList( const char *pchNames, const char *pchIndexName, int nFlags, int nFillFactor );
|
||||
|
||||
// We want a unique index on a particular field
|
||||
int UniqueField( const char *pchName, const char *pchIndexName );
|
||||
|
||||
// We want to have a clustered index on a particular field by name
|
||||
int ClusteredIndexField( int nFillFactor, const char *pchName, const char *pchIndexName );
|
||||
|
||||
// We want to index a particular list of fields in a group
|
||||
int ClusteredIndexFields( int nFillFactor, const char *pchIndexName, const char *pchNames );
|
||||
|
||||
// We want an autoinc on a particular field
|
||||
void AutoIncrementField( char *pchName );
|
||||
|
||||
// catalog on which we'll enable FTS
|
||||
void EnableFTS( ESchemaCatalog eCatalog );
|
||||
|
||||
// adds a full text catalog with the given name on the identified fileset
|
||||
void AddFullTextCatalog( ESchemaCatalog eCatalog, const char *pstrCatalogName, const char *pstrFileGroupName );
|
||||
|
||||
// Adds a FK on the table
|
||||
void AddFK( const char* pchName, const char* pchColumn, const char* pchParentTable, const char* pchParentColumn, EForeignKeyAction eOnDeleteAction, EForeignKeyAction eOnUpdateAction );
|
||||
|
||||
// Access FK data
|
||||
int GetFKCount();
|
||||
FKData_t &GetFKData( int iIndex );
|
||||
|
||||
void SetTestWipePolicy( EWipePolicy policy ) { m_wipePolicy = policy; }
|
||||
EWipePolicy GetTestWipePolicy() const { return m_wipePolicy; }
|
||||
|
||||
void SetBAllowWipeTableInProd( bool bVal ) { m_bAllowWipeInProd = bVal; }
|
||||
bool BAllowWipeTableInProd() const { return m_bAllowWipeInProd; }
|
||||
|
||||
void SetPrepopulatedTable( ) { m_bPrepopulatedTable = true; }
|
||||
bool BPrepopulatedTable( ) const { return m_bPrepopulatedTable; }
|
||||
|
||||
// Find the field with a given name (returns k_iFieldNil if not found)
|
||||
int FindIField( const char *pchName );
|
||||
int FindIFieldSQL( const char *pchName );
|
||||
|
||||
// Helper functions for recording schema conversion operations
|
||||
void AddDeleteField( const char *pchFieldName );
|
||||
void AddRenameField( const char *pchFieldNameOld, const char *pchFieldNameNew );
|
||||
void AddAlterField( const char *pchFieldNameOld, const char *pchFieldNameNew, PfnAlterField_t pfnAlterField );
|
||||
|
||||
// Schema conversion helper: figure out what field to map a field from a different schema to
|
||||
bool BCanConvertField( const char *pchFieldSrc, int *piFieldDst, PfnAlterField_t *ppfnAlterField );
|
||||
|
||||
CRecordInfo *GetRecordInfo() { return m_pRecordInfo; }
|
||||
const CRecordInfo *GetRecordInfo() const { return m_pRecordInfo; }
|
||||
|
||||
void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
void ValidateRecord( uint8 *pubRecord ); // Validate a record that uses our schema
|
||||
|
||||
private:
|
||||
int m_iTable; // The index of our table
|
||||
int m_iPKIndex; // index into of m_VecIndexes of our PK index; k_iFieldNil if no PK
|
||||
char m_rgchName[k_cSQLObjectNameMax]; // Name of this table
|
||||
CUtlVector<Field_t> m_VecField; // All the fields that make up the schema
|
||||
CUtlVector<FieldSet_t> m_VecIndexes; // vector of all fields in all indexes
|
||||
int m_cRecordMax; // Max # records in the table (for rolling tables)
|
||||
bool m_bTestTable; // Table exists only for tests
|
||||
bool m_bAllowWipeInProd; // should we allow WipeTable operations on this table in the beta/public universe?
|
||||
EWipePolicy m_wipePolicy; // should this table be wiped between all tests, no tests, or non-stress tests?
|
||||
bool m_bHasVarFields; // True if this table has variable-length fields
|
||||
bool m_bPrepopulatedTable; // true if this table is pre-populated
|
||||
EPrimaryKeyType m_nHasPrimaryKey; // what kind of PK do we have, if any?
|
||||
CRecordInfo *m_pRecordInfo; // The record description corresponding to this schema. (Similar info, record description is new form, have both for a while during DS->SQL switch)
|
||||
CUtlVector<int> m_VecFullTextIndexes; // vector of indexes into m_VecField of fields covered by this table's full-text index.
|
||||
int m_nFullTextIndexCatalog; // index of catalog to use for creating full-text indexes
|
||||
CUtlVector<FKData_t> m_VecFKData; // data on foreign keys for this schema object
|
||||
|
||||
uint32 m_cubRecord; // Binary record length
|
||||
int m_nReportingInterval; // reporting interval of this table if stats; 0 if not stats
|
||||
ESchemaCatalog m_eSchemaCatalog; // what catalog does this table live in?
|
||||
|
||||
// Schema conversion instructions
|
||||
CUtlVector<DeleteField_t> m_VecDeleteField;
|
||||
CUtlVector<RenameField_t> m_VecRenameField;
|
||||
CUtlVector<AlterField_t> m_VecAlterField;
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // GCSCHEMA_H
|
||||
283
public/gcsdk/sqlaccess/schemafull.h
Normal file
283
public/gcsdk/sqlaccess/schemafull.h
Normal file
@@ -0,0 +1,283 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSCHEMAFULL_H
|
||||
#define GCSCHEMAFULL_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SerSchemaFull
|
||||
// This defines the binary serialization format for a CSchemaFull
|
||||
//-----------------------------------------------------------------------------
|
||||
struct SerSchemaFull_t
|
||||
{
|
||||
enum EVersion
|
||||
{
|
||||
k_ECurrentVersion = 1,
|
||||
};
|
||||
|
||||
int32 m_nVersion; // version of serialization format
|
||||
int32 m_cSchema; // # of schema we contain
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CFTSCatalogInfo
|
||||
// information about a full text search catalog object in our schema
|
||||
//-----------------------------------------------------------------------------
|
||||
class CFTSCatalogInfo
|
||||
{
|
||||
public:
|
||||
enum ESchemaCatalog m_eCatalog;
|
||||
const char *m_pstrName;
|
||||
int m_nFileGroup;
|
||||
|
||||
CFTSCatalogInfo()
|
||||
: m_pstrName( NULL ),
|
||||
m_eCatalog( k_ESchemaCatalogInvalid )
|
||||
{
|
||||
}
|
||||
|
||||
~CFTSCatalogInfo()
|
||||
{
|
||||
free( (void*) m_pstrName);
|
||||
}
|
||||
|
||||
CFTSCatalogInfo( const CFTSCatalogInfo &refOther )
|
||||
{
|
||||
m_eCatalog = refOther.m_eCatalog;
|
||||
m_nFileGroup = refOther.m_nFileGroup;
|
||||
if ( refOther.m_pstrName != NULL )
|
||||
m_pstrName = strdup( refOther.m_pstrName );
|
||||
else
|
||||
m_pstrName = NULL;
|
||||
}
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName ) // Validate our internal structures
|
||||
{
|
||||
validator.ClaimMemory( (void *) m_pstrName );
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SchemaFull conversion instructions
|
||||
// These specify various operations that can be performed when converting
|
||||
// from one SchemaFull to another.
|
||||
//-----------------------------------------------------------------------------
|
||||
struct DeleteTable_t
|
||||
{
|
||||
char m_rgchTableName[k_cSQLObjectNameMax]; // Name of the table to delete
|
||||
};
|
||||
|
||||
struct RenameTable_t
|
||||
{
|
||||
char m_rgchTableNameOld[k_cSQLObjectNameMax]; // Rename a table with this name
|
||||
int m_iTableDst; // to this table
|
||||
};
|
||||
|
||||
|
||||
enum ETriggerType
|
||||
{
|
||||
k_ETriggerType_Invalid,
|
||||
k_ETriggerType_After_Insert,
|
||||
k_ETriggerType_InsteadOf_Insert,
|
||||
k_ETriggerType_After_Delete,
|
||||
k_ETriggerType_InsteadOf_Delete,
|
||||
k_ETriggerType_After_Update,
|
||||
k_ETriggerType_InsteadOf_Update,
|
||||
};
|
||||
|
||||
class CTriggerInfo
|
||||
{
|
||||
public:
|
||||
CTriggerInfo()
|
||||
: m_eTriggerType( k_ETriggerType_Invalid ),
|
||||
m_bMatched( false )
|
||||
{
|
||||
}
|
||||
|
||||
// are these equal for identity?
|
||||
bool operator==( const CTriggerInfo& refOther ) const
|
||||
{
|
||||
if ( 0 != Q_stricmp( m_szTriggerTableName, refOther.m_szTriggerTableName ) )
|
||||
return false;
|
||||
if ( 0 != Q_stricmp( m_szTriggerName, refOther.m_szTriggerName ) )
|
||||
return false;
|
||||
|
||||
// they're equal!
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the identity is the same, this will tell if text or type differs
|
||||
bool IsDifferent( const CTriggerInfo& refOther ) const
|
||||
{
|
||||
if ( m_eTriggerType != refOther.m_eTriggerType )
|
||||
return false;
|
||||
if ( m_strText != refOther.m_strText )
|
||||
return false;
|
||||
|
||||
// they're equal!
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* GetTriggerTypeString() const
|
||||
{
|
||||
const char *pstrSQL = "~~ unknown trigger type syntax error ~~";
|
||||
|
||||
switch ( m_eTriggerType )
|
||||
{
|
||||
case k_ETriggerType_After_Insert:
|
||||
pstrSQL = "AFTER INSERT";
|
||||
break;
|
||||
case k_ETriggerType_InsteadOf_Insert:
|
||||
pstrSQL = "INSTEAD OF INSERT";
|
||||
break;
|
||||
case k_ETriggerType_After_Delete:
|
||||
pstrSQL = "AFTER DELETE";
|
||||
break;
|
||||
case k_ETriggerType_InsteadOf_Delete:
|
||||
pstrSQL = "INSTEAD OF DELETE";
|
||||
break;
|
||||
case k_ETriggerType_After_Update:
|
||||
pstrSQL = "AFTER UPDATE";
|
||||
break;
|
||||
case k_ETriggerType_InsteadOf_Update:
|
||||
pstrSQL = "INSTEAD OF UPDATE";
|
||||
break;
|
||||
|
||||
default:
|
||||
case k_ETriggerType_Invalid:
|
||||
/* initialize is fine, thanks */
|
||||
break;
|
||||
}
|
||||
|
||||
return pstrSQL;
|
||||
}
|
||||
|
||||
bool m_bMatched; // got matched during schema convert
|
||||
ETriggerType m_eTriggerType; // what kinda trigger is this?
|
||||
ESchemaCatalog m_eSchemaCatalog; // catalog where this trigger lives
|
||||
char m_szTriggerName[k_cSQLObjectNameMax]; // name of the trigger object
|
||||
char m_szTriggerTableName[k_cSQLObjectNameMax]; // name of the table hosting this trigger
|
||||
CUtlString m_strText; // text of the trigger
|
||||
|
||||
// Validate our internal structures
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName )
|
||||
{
|
||||
m_strText.Validate( validator, pchName );
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CSchemaFull
|
||||
// This defines the schema for the entire data store. It's essentially just
|
||||
// a collection of CSchema, which define the schema for individual tables.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSchemaFull
|
||||
{
|
||||
public:
|
||||
// Constructors & destructors
|
||||
CSchemaFull();
|
||||
~CSchemaFull();
|
||||
|
||||
void Uninit();
|
||||
|
||||
// add a new schema and return its pointer.
|
||||
CSchema *AddNewSchema( int iTable, ESchemaCatalog eCatalog, const char *pstrName )
|
||||
{
|
||||
CSchema &refNewSchema = m_VecSchema[m_VecSchema.AddToTail()];
|
||||
refNewSchema.SetName( pstrName );
|
||||
refNewSchema.SetESchemaCatalog( eCatalog );
|
||||
SetITable( &refNewSchema, iTable );
|
||||
return &refNewSchema;
|
||||
}
|
||||
|
||||
|
||||
// Accessors
|
||||
int GetCSchema() const { return m_VecSchema.Count(); }
|
||||
CSchema &GetSchema( int iSchema ) { return m_VecSchema[iSchema]; }
|
||||
uint32 GetCheckSum() const { return m_unCheckSum; }
|
||||
|
||||
uint8 *GetPubScratchBuffer( );
|
||||
uint32 GetCubScratchBuffer() const { return m_cubScratchBuffer; }
|
||||
|
||||
// Makes sure that a generated intrinsic schema is consistent
|
||||
void CheckSchema( CSchema *pSchema, int cField, uint32 cubRecord );
|
||||
|
||||
// Find the table with a given name (returns -1 if not found)
|
||||
int FindITable( const char *pchName );
|
||||
const char *PchTableFromITable( int iTable );
|
||||
|
||||
// Helper functions for recording schema conversion operations
|
||||
void AddDeleteTable( const char *pchTableName );
|
||||
void AddRenameTable( const char *pchTableNameOld, const char *pchTableNameNew );
|
||||
void AddDeleteField( const char *pchTableName, const char *pchFieldName );
|
||||
void AddRenameField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldNameNew );
|
||||
void AddAlterField( const char *pchTableName, const char *pchFieldNameOld, const char *pchFieldNameNew, PfnAlterField_t pfnAlterField );
|
||||
|
||||
// declare that a trigger is on a table
|
||||
void AddTrigger( ESchemaCatalog eCatalog, const char *pchTableName, const char *pchTriggerName, ETriggerType eTriggerType, const char *pchTriggerText );
|
||||
|
||||
// Schema conversion helper: figure out what table to map a table from a different schema to
|
||||
bool BCanConvertTable( const char *pchTableSrc, int *piTableDst );
|
||||
|
||||
// full text catalogs
|
||||
void AddFullTextCatalog( enum ESchemaCatalog eCatalog, const char *pstrCatalogName, int nFileGroup );
|
||||
int GetFTSCatalogByName( enum ESchemaCatalog eCatalog, const char *pstrCatalogName );
|
||||
void EnableFTS( enum ESchemaCatalog eCatalog );
|
||||
int GetCFTSCatalogs() const { return m_vecFTSCatalogs.Count(); }
|
||||
const CFTSCatalogInfo & GetFTSCatalogInfo( int nIndex ) const { return m_vecFTSCatalogs[nIndex]; }
|
||||
|
||||
const CUtlVector< CTriggerInfo> & GetTriggerInfos( ) const { return m_VecTriggers; }
|
||||
|
||||
// is the given schema catalog FTS enabled?
|
||||
bool GetFTSEnabled( enum ESchemaCatalog eCatalog );
|
||||
|
||||
void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
|
||||
|
||||
// sets tableID on CSchema, checking that it is not a duplicate
|
||||
void SetITable( CSchema* pSchema, int iTable );
|
||||
void FinishInit(); // Recalculates some internal fields
|
||||
private:
|
||||
|
||||
|
||||
CUtlVector< CSchema > m_VecSchema; // Schema for tables in all catalogs
|
||||
CUtlVector< CTriggerInfo > m_VecTriggers; // list of triggers in all catalogs
|
||||
|
||||
// which schema catalogs have FTS enabled?
|
||||
CUtlMap< ESchemaCatalog, bool > m_mapFTSEnabled;
|
||||
|
||||
// list of catalogs; each is marked with the schema where it lives.
|
||||
CUtlVector< CFTSCatalogInfo > m_vecFTSCatalogs;
|
||||
|
||||
uint32 m_unCheckSum; // A simple checksum of our contents
|
||||
|
||||
// SchemaFull conversion instructions
|
||||
CUtlVector<DeleteTable_t> m_VecDeleteTable;
|
||||
CUtlVector<RenameTable_t> m_VecRenameTable;
|
||||
|
||||
uint8 *m_pubScratchBuffer; // Big enough to hold any record or sparse record in this schemafull
|
||||
uint32 m_cubScratchBuffer; // Size of the scratch buffer
|
||||
};
|
||||
|
||||
extern CSchemaFull & GSchemaFull();
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // GCSCHEMAFULL_H
|
||||
91
public/gcsdk/sqlaccess/schemaupdate.h
Normal file
91
public/gcsdk/sqlaccess/schemaupdate.h
Normal file
@@ -0,0 +1,91 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Contains the job that's responsible for updating the database schema
|
||||
//
|
||||
//=============================================================================
|
||||
#ifndef UPDATESCHEMA_H
|
||||
#define UPDATESCHEMA_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
typedef CUtlMap<const char *,CRecordInfo *> CMapPRecordInfo;
|
||||
|
||||
enum EConversionMode
|
||||
{
|
||||
k_EConversionModeInspectOnly,
|
||||
k_EConversionModeConvertSafe,
|
||||
k_EConversionModeConvertIrreversible
|
||||
};
|
||||
|
||||
class CSchemaUpdate : public CRefCount
|
||||
{
|
||||
public:
|
||||
CSchemaUpdate();
|
||||
|
||||
void AddRecordInfoDesired( CRecordInfo *pRecordInfo );
|
||||
void AddFTSInfo( const CFTSCatalogInfo &refFTSInfo );
|
||||
void AddTriggerInfos( const CUtlVector< CTriggerInfo > &refTriggerInfo );
|
||||
|
||||
// input parameters
|
||||
CMapPRecordInfo m_mapPRecordInfoDesired;
|
||||
EConversionMode m_eConversionMode;
|
||||
CUtlLinkedList< CFTSCatalogInfo > m_listFTSCatalogInfo;
|
||||
CUtlVector< CTriggerInfo > m_vecTriggerInfo;
|
||||
|
||||
// output parameters
|
||||
bool m_bConversionNeeded;
|
||||
bool m_bSkippedAChange;
|
||||
int m_cTablesDesiredMissing;
|
||||
int m_cTablesActualDifferent;
|
||||
int m_cTablesActualUnknown;
|
||||
int m_cTablesNeedingChange;
|
||||
int m_cColumnsDesiredMissing;
|
||||
int m_cColumnsActualDifferent;
|
||||
int m_cColumnsActualUnknown;
|
||||
|
||||
CFmtStr1024 m_sDetail;
|
||||
|
||||
private:
|
||||
virtual ~CSchemaUpdate();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
class CJobUpdateSchema : public CGCJob
|
||||
{
|
||||
public:
|
||||
CJobUpdateSchema( CGCBase *pGC, int iTableCount ) : CGCJob( pGC ), m_mapSQLTypeToEType( DefLessFunc(int) ), m_iTableCount( iTableCount ) { }
|
||||
bool BYieldingRunJob( void * );
|
||||
private:
|
||||
bool BYieldingUpdateSchema( ESchemaCatalog eSchemaCatalog );
|
||||
SQLRETURN YieldingEnsureDatabaseSchemaCorrect( ESchemaCatalog eSchemaCatalog, CSchemaUpdate *pSchemaUpdate );
|
||||
EGCSQLType GetEGCSQLTypeForMSSQLType( int nType );
|
||||
bool YieldingBuildTypeMap( ESchemaCatalog eSchemaCatalog );
|
||||
SQLRETURN YieldingGetSchemaID( ESchemaCatalog eSchemaCatalog, int *pSchemaID );
|
||||
SQLRETURN YieldingGetRecordInfoForAllTables( ESchemaCatalog eSchemaCatalog, int nSchemaID, CMapPRecordInfo &mapPRecordInfo );
|
||||
SQLRETURN YieldingGetColumnInfoForTable( ESchemaCatalog eSchemaCatalog, CMapPRecordInfo &mapPRecordInfo, int nTableID, const char *pchTableName );
|
||||
SQLRETURN YieldingGetTableFKConstraints( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo );
|
||||
SQLRETURN YieldingGetColumnIndexes( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo );
|
||||
SQLRETURN YieldingGetTriggers( ESchemaCatalog eSchemaCatalog, int nSchemaID, CUtlVector< CTriggerInfo > &vecTriggerInfo );
|
||||
SQLRETURN YieldingCreateTable( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo );
|
||||
SQLRETURN YieldingAddIndex( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo );
|
||||
SQLRETURN YieldingAddIndex( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const FieldSet_t &refFields );
|
||||
SQLRETURN YieldingRemoveIndex( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo );
|
||||
SQLRETURN YieldingAlterTableAddColumn( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo );
|
||||
SQLRETURN YieldingAddConstraint( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo, int nColFlagConstraint );
|
||||
SQLRETURN YieldingRemoveConstraint( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo, int nColFlagConstraint );
|
||||
SQLRETURN YieldingChangeColumnTypeOrLength( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfoDesired );
|
||||
SQLRETURN YieldingChangeColumnProperties( ESchemaCatalog eSchemaCatalog, CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfoActual, const CColumnInfo *pColumnInfoDesired );
|
||||
SQLRETURN YieldingCreateTrigger( ESchemaCatalog eSchemaCatalog, CTriggerInfo &refTriggerInfo );
|
||||
SQLRETURN YieldingDropTrigger( ESchemaCatalog eSchemaCatalog, CTriggerInfo &refTriggerInfo );
|
||||
|
||||
CUtlMap<int,EGCSQLType> m_mapSQLTypeToEType;
|
||||
int m_iTableCount;
|
||||
};
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // UPDATESCHEMA_H
|
||||
354
public/gcsdk/sqlaccess/sqlaccess.h
Normal file
354
public/gcsdk/sqlaccess/sqlaccess.h
Normal file
@@ -0,0 +1,354 @@
|
||||
//====== Copyright (c), Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Provides access to SQL at a high level
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef SQLACCESS_H
|
||||
#define SQLACCESS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "gcsdk/gcsqlquery.h"
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
class CGCSQLQuery;
|
||||
class CGCSQLQueryGroup;
|
||||
class CColumnSet;
|
||||
class CRecordType;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Provides access to SQL at a high level
|
||||
//-----------------------------------------------------------------------------
|
||||
class CSQLAccess
|
||||
{
|
||||
public:
|
||||
CSQLAccess( ESchemaCatalog eSchemaCatalog = k_ESchemaCatalogMain );
|
||||
~CSQLAccess( );
|
||||
|
||||
bool BBeginTransaction( const char *pchName );
|
||||
bool BCommitTransaction( bool bAllowEmpty = false );
|
||||
void RollbackTransaction();
|
||||
bool BInTransaction( ) const { return m_bInTransaction; }
|
||||
|
||||
bool BYieldingExecute( const char *pchName, const char *pchSQLCommand, uint32 *pcRowsAffected = NULL, bool bSpewOnError = true );
|
||||
bool BYieldingExecuteString( const char *pchName, const char *pchSQLCommand, CFmtStr1024 *psResult, uint32 *pcRowsAffected = NULL );
|
||||
bool BYieldingExecuteScalarInt( const char *pchName, const char *pchSQLCommand, int *pnResult, uint32 *pcRowsAffected = NULL );
|
||||
bool BYieldingExecuteScalarIntWithDefault( const char *pchName, const char *pchSQLCommand, int *pnResult, int iDefaultValue, uint32 *pcRowsAffected = NULL );
|
||||
bool BYieldingExecuteScalarUint32( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 *pcRowsAffected = NULL );
|
||||
bool BYieldingExecuteScalarUint32WithDefault( const char *pchName, const char *pchSQLCommand, uint32 *punResult, uint32 unDefaultValue, uint32 *pcRowsAffected = NULL );
|
||||
bool BYieldingWipeTable( int iTable );
|
||||
|
||||
template <typename TReturn, typename TCast>
|
||||
bool BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected );
|
||||
template <typename TReturn, typename TCast>
|
||||
bool BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected );
|
||||
|
||||
// manipulating CRecordBase (i.e. CSch...) objects in the database
|
||||
bool BYieldingInsertRecord( CRecordBase *pRecordBase );
|
||||
bool BYieldingInsertWithIdentity( CRecordBase* pRecordBase ) ;
|
||||
bool BYieldingReadRecordWithWhereColumns( CRecordBase *pRecord, const CColumnSet & readSet, const CColumnSet & whereSet );
|
||||
|
||||
template< typename SchClass_t >
|
||||
bool BYieldingReadRecordFromPK( SchClass_t *pRecord );
|
||||
template< typename SchClass_t>
|
||||
bool BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords, const CColumnSet & whereSet, CUtlVector< SchClass_t > *pvecUnmatchedRecords = NULL );
|
||||
template< typename SchClass_t>
|
||||
bool BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords, const CColumnSet & readSet, const CColumnSet & whereSet, CUtlVector< SchClass_t > *pvecUnmatchedRecords = NULL );
|
||||
template< typename SchClass_t>
|
||||
bool BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause = NULL );
|
||||
template< typename SchClass_t>
|
||||
bool BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet );
|
||||
bool BYieldingUpdateRecord( const CRecordBase &record, const CColumnSet & whereColumns, const CColumnSet & updateColumns );
|
||||
bool BYieldingDeleteRecord( const CRecordBase & record, const CColumnSet & whereColumns );
|
||||
|
||||
void AddRecordParameters( const CRecordBase &record, const CColumnSet & columnSet );
|
||||
|
||||
void AddBindParam( const char *pchValue );
|
||||
void AddBindParam( const int16 nValue );
|
||||
void AddBindParam( const uint16 uValue );
|
||||
void AddBindParam( const int32 nValue );
|
||||
void AddBindParam( const uint32 uValue );
|
||||
void AddBindParam( const uint64 ulValue );
|
||||
void AddBindParam( const uint8 *ubValue, const int cubValue );
|
||||
void AddBindParam( const float fValue );
|
||||
void AddBindParam( const double dValue );
|
||||
void ClearParams();
|
||||
IGCSQLResultSetList *GetResults();
|
||||
|
||||
uint32 GetResultSetCount();
|
||||
uint32 GetResultSetRowCount( uint32 unResultSet );
|
||||
CSQLRecord GetResultRecord( uint32 unResultSet, uint32 unRow );
|
||||
|
||||
private:
|
||||
enum EReadSingleResultResult
|
||||
{
|
||||
eReadSingle_Error, // something went wrong in the DB or the data was in a format we didn't expect
|
||||
eReadSingle_ResultFound, // we found a single result and copied the value -- all is well!
|
||||
eReadSingle_UseDefault, // we didn't find any results but we specified a value in advance for this case
|
||||
};
|
||||
|
||||
EReadSingleResultResult BYieldingExecuteSingleResultDataInternal( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, uint8 **pubData, uint32 *punSize, uint32 *pcRowsAffected, bool bHasDefaultValue );
|
||||
|
||||
private:
|
||||
|
||||
CGCSQLQuery *CurrentQuery();
|
||||
ESchemaCatalog m_eSchemaCatalog;
|
||||
CGCSQLQuery *m_pCurrentQuery;
|
||||
CGCSQLQueryGroup *m_pQueryGroup;
|
||||
bool m_bInTransaction;
|
||||
};
|
||||
|
||||
#define FOR_EACH_SQL_RESULT( sqlAccess, resultSet, record ) \
|
||||
for( CSQLRecord record = (sqlAccess).GetResultRecord( resultSet, 0 ); record.IsValid(); record.NextRow() )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: templatized version of querying for a single value
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename TReturn, typename TCast>
|
||||
bool CSQLAccess::BYieldingExecuteSingleResult( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, uint32 *pcRowsAffected )
|
||||
{
|
||||
uint8 *pubData;
|
||||
uint32 cubData;
|
||||
if( CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, false ) != eReadSingle_ResultFound )
|
||||
return false;
|
||||
|
||||
*pResult = *( (TCast *)pubData );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: templatized version of querying for a single value
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename TReturn, typename TCast>
|
||||
bool CSQLAccess::BYieldingExecuteSingleResultWithDefault( const char *pchName, const char *pchSQLCommand, EGCSQLType eType, TReturn *pResult, TReturn defaultValue, uint32 *pcRowsAffected )
|
||||
{
|
||||
uint8 *pubData;
|
||||
uint32 cubData;
|
||||
EReadSingleResultResult eResult = CSQLAccess::BYieldingExecuteSingleResultDataInternal( pchName, pchSQLCommand, eType, &pubData, &cubData, pcRowsAffected, true );
|
||||
|
||||
if ( eResult == eReadSingle_Error )
|
||||
return false;
|
||||
|
||||
if ( eResult == eReadSingle_ResultFound )
|
||||
{
|
||||
*pResult = *( (TCast *)pubData );
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( eResult == eReadSingle_UseDefault );
|
||||
*pResult = defaultValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads the record with a given PK.
|
||||
// Input: pRecordBase - record to read
|
||||
// Output: true if successful, false otherwise
|
||||
//-----------------------------------------------------------------------------
|
||||
template < typename SchClass_t >
|
||||
bool CSQLAccess::BYieldingReadRecordFromPK( SchClass_t *pRecord )
|
||||
{
|
||||
CColumnSet csetWhere = CColumnSet::PrimaryKey< SchClass_t >();
|
||||
CColumnSet csetRead = CColumnSet::Inverse( csetWhere );
|
||||
return BYieldingReadRecordWithWhereColumns( pRecord, csetRead, csetWhere );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads multiple records from the database based on the where columns
|
||||
// filled in for each record. If the record is not found in the database
|
||||
// it will be removed from pvecRecords. If pvecUnmatchedRecords is
|
||||
// provided, it will be populated with the unmatched records removed
|
||||
// from pvecRecords
|
||||
// Input: pvecRecords - The records to fill in from the database
|
||||
// whereSet - The set of columns to query on
|
||||
// (optional) pvecUnmatchedRecords - A vector to hold records which
|
||||
// are not found in the database
|
||||
// Output: true if successful, false otherwise
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename SchClass_t>
|
||||
bool CSQLAccess::BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords,
|
||||
const CColumnSet & whereSet,
|
||||
CUtlVector< SchClass_t > *pvecUnmatchedRecords /* = NULL */ )
|
||||
{
|
||||
CColumnSet readSet( GSchemaFull().GetSchema( SchClass_t::k_iTable ).GetRecordInfo() );
|
||||
readSet.MakeInverse( whereSet );
|
||||
return BYieldingReadMultipleRecordsWithWhereColumns( pvecRecords, readSet, whereSet, pvecUnmatchedRecords );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads multiple records from the database based on the where columns
|
||||
// filled in for each record. If the record is not found in the database
|
||||
// it will be removed from pvecRecords. If pvecUnmatchedRecords is
|
||||
// provided, it will be populated with the unmatched records removed
|
||||
// from pvecRecords
|
||||
// Input: pvecRecords - The records to fill in from the database
|
||||
// readSet - The set of columns to fill in
|
||||
// whereSet - The set of columns to query on
|
||||
// (optional) pvecUnmatchedRecords - A vector to hold records which
|
||||
// are not found in the database
|
||||
// Output: true if successful, false otherwise
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename SchClass_t>
|
||||
bool CSQLAccess::BYieldingReadMultipleRecordsWithWhereColumns( CUtlVector< SchClass_t > *pvecRecords,
|
||||
const CColumnSet & readSet,
|
||||
const CColumnSet & whereSet,
|
||||
CUtlVector< SchClass_t > *pvecUnmatchedRecords /* = NULL */ )
|
||||
{
|
||||
AssertMsg( !BInTransaction(), "BYieldingReadMultipleRecordsWithWhereColumns is not supported in a transaction" );
|
||||
if( BInTransaction() )
|
||||
return false;
|
||||
|
||||
Assert( !readSet.IsEmpty() );
|
||||
if ( readSet.IsEmpty() )
|
||||
return false;
|
||||
|
||||
if ( pvecUnmatchedRecords )
|
||||
{
|
||||
pvecUnmatchedRecords->RemoveAll();
|
||||
}
|
||||
|
||||
// Build the query we'll use for each record
|
||||
CFmtStr1024 sStatement, sWhere;
|
||||
BuildSelectStatementText( &sStatement, readSet );
|
||||
BuildWhereClauseText( &sWhere, whereSet );
|
||||
sStatement.Append( " WHERE " );
|
||||
sStatement.Append( sWhere );
|
||||
|
||||
BBeginTransaction( CFmtStr1024( "BYieldingReadMultipleRecordsWithWhereColumns() - %s", sStatement.Access() ) );
|
||||
|
||||
// Batch this query for each record
|
||||
FOR_EACH_VEC( *pvecRecords, i )
|
||||
{
|
||||
AddRecordParameters( pvecRecords->Element( i ), whereSet );
|
||||
if( !BYieldingExecute( NULL, sStatement ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
// Actually run the query
|
||||
if ( !BCommitTransaction() )
|
||||
return false;
|
||||
|
||||
Assert( GetResultSetCount() == (uint32)pvecRecords->Count() );
|
||||
if ( GetResultSetCount() != (uint32)pvecRecords->Count() )
|
||||
return false;
|
||||
|
||||
// Get the results. Reading backwards because if a record doesn't find a match we'll
|
||||
// remove it from the list
|
||||
FOR_EACH_VEC_BACK( *pvecRecords, i )
|
||||
{
|
||||
// make sure the types are the same
|
||||
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( i );
|
||||
Assert( pResultSet->GetRowCount() <= 1 );
|
||||
if ( pResultSet->GetRowCount() > 1 )
|
||||
return false;
|
||||
|
||||
if( pResultSet->GetRowCount() == 1 )
|
||||
{
|
||||
// We have a record in this set, read it in
|
||||
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
|
||||
{
|
||||
EGCSQLType eRecordType = readSet.GetColumnInfo( nColumnIndex ).GetType();
|
||||
EGCSQLType eResultType = pResultSet->GetColumnType( nColumnIndex );
|
||||
|
||||
Assert( eResultType == eRecordType );
|
||||
if( eRecordType != eResultType )
|
||||
return false;
|
||||
}
|
||||
|
||||
CSQLRecord sqlRecord = GetResultRecord( i, 0 );
|
||||
|
||||
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
|
||||
{
|
||||
uint8 *pubData;
|
||||
uint32 cubData;
|
||||
|
||||
DbgVerify( sqlRecord.BGetColumnData( nColumnIndex, &pubData, (int*)&cubData ) );
|
||||
DbgVerify( pvecRecords->Element( i ).BSetField( readSet.GetColumn( nColumnIndex ), pubData, cubData ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This record did not match, remove it and add it to pvecUnmatchedRecords if needed
|
||||
if ( pvecUnmatchedRecords )
|
||||
{
|
||||
pvecUnmatchedRecords->AddToTail( pvecRecords->Element( i ) );
|
||||
}
|
||||
|
||||
pvecRecords->Remove( i );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Reads a list of records from the DB according to the specified where
|
||||
// clause
|
||||
// Input: pRecordBase - record to insert
|
||||
// Output: true if successful, false otherwise
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename SchClass_t>
|
||||
bool CSQLAccess::BYieldingReadRecordsWithWhereClause( CUtlVector< SchClass_t > *pvecRecords, const char *pchWhereClause, const CColumnSet & readSet, const char *pchTopClause )
|
||||
{
|
||||
AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithWhereClause is not supported in a transaction" );
|
||||
if( BInTransaction() )
|
||||
return false;
|
||||
|
||||
Assert( !readSet.IsEmpty() );
|
||||
CFmtStr1024 sStatement;
|
||||
BuildSelectStatementText( &sStatement, readSet, pchTopClause );
|
||||
Assert( pchWhereClause && *pchWhereClause );
|
||||
if( !pchWhereClause || !(*pchWhereClause) )
|
||||
return false;
|
||||
|
||||
CUtlString sFullStatement = sStatement.Access();
|
||||
sFullStatement += " WHERE ";
|
||||
sFullStatement += pchWhereClause;
|
||||
|
||||
return BYieldingReadRecordsWithQuery< SchClass_t >( pvecRecords, sFullStatement, readSet );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Inserts a new record into the DB and reads non-insertable fields back
|
||||
// into the record.
|
||||
// Input: pRecordBase - record to insert
|
||||
// Output: true if successful, false otherwise
|
||||
//-----------------------------------------------------------------------------
|
||||
template< typename SchClass_t>
|
||||
bool CSQLAccess::BYieldingReadRecordsWithQuery( CUtlVector< SchClass_t > *pvecRecords, const char *sQuery, const CColumnSet & readSet )
|
||||
{
|
||||
AssertMsg( !BInTransaction(), "BYieldingReadRecordsWithQuery is not supported in a transaction" );
|
||||
if( BInTransaction() )
|
||||
return false;
|
||||
|
||||
Assert(!readSet.IsEmpty() );
|
||||
if( !BYieldingExecute( NULL, sQuery ) )
|
||||
return false;
|
||||
|
||||
Assert( GetResultSetCount() == 1 );
|
||||
if ( GetResultSetCount() != 1 )
|
||||
return false;
|
||||
|
||||
// make sure the types are the same
|
||||
IGCSQLResultSet *pResultSet = m_pQueryGroup->GetResults()->GetResultSet( 0 );
|
||||
return CopyResultToSchVector( pResultSet, readSet, pvecRecords );
|
||||
}
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // SQLACCESS_H
|
||||
54
public/gcsdk/sqlaccess/sqlrecord.h
Normal file
54
public/gcsdk/sqlaccess/sqlrecord.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GCSQLRECORD_H
|
||||
#define GCSQLRECORD_H
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
class CSQLRecord
|
||||
{
|
||||
public:
|
||||
CSQLRecord( uint32 unRow, IGCSQLResultSet *pResultSet );
|
||||
CSQLRecord( );
|
||||
~CSQLRecord();
|
||||
|
||||
void Init( uint32 unRow, IGCSQLResultSet *pResultSet );
|
||||
|
||||
bool BWriteToRecord( CRecordBase *pRecord, const CColumnSet & csWriteFields );
|
||||
bool BGetColumnData( uint32 unColumn, uint8 **ppubField, int *pcubField );
|
||||
bool BGetColumnData( uint32 unColumn, uint8 **ppubField, size_t *pcubField );
|
||||
bool BGetStringValue( uint32 unColumn, const char **ppchVal );
|
||||
bool BGetStringValue( uint32 unColumn, CFmtStr1024 *psVal );
|
||||
bool BGetIntValue( uint32 unColumn, int *pnVal );
|
||||
bool BGetInt16Value( uint32 unColumn, int16 *pnVal );
|
||||
bool BGetInt64Value( uint32 unColumn, int64 *puVal );
|
||||
bool BGetUint64Value( uint32 unColumn, uint64 *puVal );
|
||||
bool BGetByteValue( uint32 unColumn, byte *pVal );
|
||||
bool BGetBoolValue( uint32 unColumn, bool *pVal );
|
||||
bool BGetUint32Value( uint32 unColumn, uint32 *puVal );
|
||||
bool BGetUint16Value( uint32 unColumn, uint16 *puVal );
|
||||
bool BGetUint8Value( uint32 unColumn, uint8 *puVal );
|
||||
bool BGetFloatValue( uint32 unColumn, float *pfVal );
|
||||
bool BGetDoubleValue( uint32 unColumn, double *pdVal );
|
||||
|
||||
void RenderField( uint32 unColumn, int cchBuffer, char *pchBuffer );
|
||||
|
||||
bool NextRow();
|
||||
bool IsValid() const { return m_pResultSet != NULL; }
|
||||
|
||||
private:
|
||||
|
||||
|
||||
bool BValidateColumnIndex( uint32 unColumn );
|
||||
IGCSQLResultSet *m_pResultSet;
|
||||
uint32 m_unRow;
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // GCSQLRECORD_H
|
||||
101
public/gcsdk/sqlaccess/sqlutil.h
Normal file
101
public/gcsdk/sqlaccess/sqlutil.h
Normal file
@@ -0,0 +1,101 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
// $NoKeywords: $
|
||||
//=============================================================================
|
||||
|
||||
#ifndef SQLUTIL_H
|
||||
#define SQLUTIL_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
// Returns a long (1024 char) string of "?,?,?,?,?"... for use in IN clauses or INSERT statements
|
||||
const char *GetInsertArgString();
|
||||
|
||||
void ConvertFieldToText( EGCSQLType eFieldType, uint8 *pubRecord, int cubRecord, char *rgchField, int cchField, bool bQuoteString = true );
|
||||
void ConvertFieldArrayToInText( const CColumnInfo &columnInfo, byte *pubData, int cubData, char *rgchResult, bool bForPreparedStatement );
|
||||
char *SQLTypeFromField( const CColumnInfo &colInfo, char *pchBuf, int cchBuf );
|
||||
void EscapeStringValue( char *rgchField, int cchField );
|
||||
void AppendConstraints( const CRecordInfo *pRecordInfo, const CColumnInfo *pColumnInfo, bool bForAdd, CFmtStrMax & sCmd );
|
||||
void AppendTableConstraints( CRecordInfo *pRecordInfo, CFmtStrMax & sCmd );
|
||||
void AppendConstraint( const char *pchTableName, const char *pchColumnName, int nColFlagConstraint, bool bForAdd, bool bClustered,
|
||||
CFmtStrMax & sCmd, int nFillFactor );
|
||||
//void BuildSelectStatementText( CUtlVector<CQuery> *pVecQuery, bool bForPreparedStatement, char *pchStatement, int cchStatement );
|
||||
void BuildInsertStatementText( CFmtStr1024 *psStatement, const CRecordInfo *pRecordInfo );
|
||||
void BuildInsertAndReadStatementText( CFmtStr1024 *psStatement, CUtlVector<int> *pvecOutputFields, const CRecordInfo *pRecordInfo ) ;
|
||||
void BuildSelectStatementText( CFmtStr1024 *psStatement, const CColumnSet & selectSet, const char *pchTopClause = NULL );
|
||||
void BuildUpdateStatementText( CFmtStr1024 *psStatement, const CColumnSet & columnSet );
|
||||
void BuildDeleteStatementText( CFmtStr1024 *psStatement, const CRecordInfo* pRecordInfo );
|
||||
void BuildWhereClauseText( CFmtStr1024 *psClause, const CColumnSet & columnSet );
|
||||
|
||||
template< typename T >
|
||||
bool CopyResultToSchVector( IGCSQLResultSet *pResultSet, const CColumnSet & readSet, CUtlVector< T > *pvecRecords )
|
||||
{
|
||||
if ( pResultSet->GetRowCount() == 0 )
|
||||
return true;
|
||||
|
||||
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
|
||||
{
|
||||
EGCSQLType eRecordType = readSet.GetColumnInfo( nColumnIndex ).GetType();
|
||||
EGCSQLType eResultType = pResultSet->GetColumnType( nColumnIndex );
|
||||
|
||||
Assert( eResultType == eRecordType );
|
||||
if( eRecordType != eResultType )
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
for( CSQLRecord sqlRecord( 0, pResultSet ); sqlRecord.IsValid(); sqlRecord.NextRow() )
|
||||
{
|
||||
int nRecord = pvecRecords->AddToTail();
|
||||
|
||||
FOR_EACH_COLUMN_IN_SET( readSet, nColumnIndex )
|
||||
{
|
||||
uint8 *pubData;
|
||||
uint32 cubData;
|
||||
|
||||
DbgVerify( sqlRecord.BGetColumnData( nColumnIndex, &pubData, (int*)&cubData ) );
|
||||
DbgVerify( pvecRecords->Element( nRecord ).BSetField( readSet.GetColumn( nColumnIndex), pubData, cubData ) );
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//EResult UpdateOrInsertUnique( CSQLAccess &sqlAccess, int iTable, int iField, CRecordBase *pRecordBase, int iIndexID );
|
||||
//bool BIsDuplicateInsertAttempt( const CSQLErrorInfo *pErr );
|
||||
|
||||
|
||||
#define EXIT_WITH_SQL_FAILURE( ret ) { nRet = ret; goto Exit; }
|
||||
|
||||
#define EXIT_ON_BOOL_FAILURE( bRet, msg ) \
|
||||
{ \
|
||||
if ( false == bRet ) \
|
||||
{ \
|
||||
SetSQLError( msg ); \
|
||||
nRet = SQL_ERROR; \
|
||||
goto Exit; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SAFE_CLOSE_STMT( x ) \
|
||||
if ( NULL != (x) ) \
|
||||
{ \
|
||||
SQLFreeHandle( SQL_HANDLE_STMT, (x) ); \
|
||||
(x) = NULL; \
|
||||
}
|
||||
|
||||
#define SAFE_FREE_HANDLE( x, y ) \
|
||||
if ( NULL != (x) ) \
|
||||
{ \
|
||||
SQLFreeHandle( y, (x) ); \
|
||||
(x) = NULL; \
|
||||
}
|
||||
|
||||
} // namespace GCSDK
|
||||
#endif // SQLUTIL_H
|
||||
26
public/gcsdk/string_misc.h
Normal file
26
public/gcsdk/string_misc.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//====== Copyright ©, Valve Corporation, All rights reserved. =======
|
||||
//
|
||||
// Purpose: Misc string helpers
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef GC_STRING_MISC_H
|
||||
#define GC_STRING_MISC_H
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
char *GetPchTempTextBuffer(); // Returns a short-lived text buffer
|
||||
size_t GetCchTempTextBuffer(); // How big is my temp text buffer?
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void ValidateTempTextBuffers( CValidator & validator );
|
||||
#endif
|
||||
|
||||
void UninitTempTextBuffers( );
|
||||
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
#endif // GC_STRING_MISC_H
|
||||
|
||||
473
public/gcsdk/webapi_response.h
Normal file
473
public/gcsdk/webapi_response.h
Normal file
@@ -0,0 +1,473 @@
|
||||
//========= Copyright © 1996-2010, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: Header for CWebAPIResponse objects
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef WEBAPI_RESPONSE_H
|
||||
#define WEBAPI_RESPONSE_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
namespace GCSDK
|
||||
{
|
||||
|
||||
enum EWebAPIOutputFormat
|
||||
{
|
||||
k_EWebAPIOutputFormat_JSON = 1,
|
||||
k_EWebAPIOutputFormat_XML = 2,
|
||||
k_EWebAPIOutputFormat_VDF = 3,
|
||||
k_EWebAPIOutputFormat_ParameterEncoding = 4,
|
||||
};
|
||||
|
||||
enum EWebAPIValueType
|
||||
{
|
||||
// Object is the initial value
|
||||
k_EWebAPIValueType_Object = 0,
|
||||
k_EWebAPIValueType_Int32 = 1,
|
||||
k_EWebAPIValueType_Int64 = 2,
|
||||
k_EWebAPIValueType_UInt32 = 3,
|
||||
k_EWebAPIValueType_UInt64 = 4,
|
||||
k_EWebAPIValueType_Double = 5,
|
||||
k_EWebAPIValueType_String = 6,
|
||||
k_EWebAPIValueType_BinaryBlob = 7,
|
||||
k_EWebAPIValueType_Bool = 8,
|
||||
k_EWebAPIValueType_Null = 9,
|
||||
k_EWebAPIValueType_NumericArray = 10,
|
||||
};
|
||||
|
||||
class CWebAPIValues;
|
||||
|
||||
class CWebAPIResponse
|
||||
{
|
||||
public:
|
||||
CWebAPIResponse();
|
||||
~CWebAPIResponse();
|
||||
|
||||
// Set the HTTP status code for the response
|
||||
void SetStatusCode( EHTTPStatusCode eStatusCode ) { m_eStatusCode = eStatusCode; }
|
||||
|
||||
// Set how many seconds until this response expires
|
||||
void SetExpirationSeconds( uint32 unExpirationSeconds ) { m_unExpirationSeconds = unExpirationSeconds; }
|
||||
|
||||
// Set when this response was last modified
|
||||
void SetLastModified( RTime32 rtLastModified ) { m_rtLastModified = rtLastModified; }
|
||||
|
||||
// Get the status code for the response
|
||||
EHTTPStatusCode GetStatusCode() { return m_eStatusCode; }
|
||||
|
||||
// Get how many seconds until this response expires
|
||||
uint32 GetExpirationSeconds() { return m_unExpirationSeconds; }
|
||||
|
||||
// Get when the response was last modified
|
||||
RTime32 GetLastModified() { return m_rtLastModified; }
|
||||
|
||||
// extended arrays include their element name as an object in JSON and VDF output formats
|
||||
void SetExtendedArrays( bool bExtendedArrays ) { m_bExtendedArrays = bExtendedArrays; }
|
||||
bool HasExtendedArrays() const { return m_bExtendedArrays; }
|
||||
|
||||
// Outputs formatted data to buffer
|
||||
bool BEmitFormattedOutput( EWebAPIOutputFormat eFormat, CUtlBuffer &outputBuffer, size_t unMaxResultSize );
|
||||
|
||||
// Resets the response to be empty
|
||||
void Clear();
|
||||
|
||||
// Create the root value element in the response
|
||||
CWebAPIValues *CreateRootValue( const char *pchName );
|
||||
CWebAPIValues *GetRootValue() { return m_pValues; }
|
||||
|
||||
private:
|
||||
|
||||
// Emits JSON formatted representation of response
|
||||
bool BEmitJSON( CUtlBuffer &outputBuffer, size_t unMaxResultSize );
|
||||
|
||||
// Emits KeyValues .vdf style formatted representation of response
|
||||
bool BEmitVDF( CUtlBuffer &outputBuffer, size_t unMaxResultSize );
|
||||
|
||||
// Emits XML formatted representation of response
|
||||
bool BEmitXML( CUtlBuffer &outputBuffer, size_t unMaxResultSize );
|
||||
|
||||
// parameter encoding, as used in a lot of open standards
|
||||
bool BEmitParameterEncoding( CUtlBuffer &outputBuffer );
|
||||
|
||||
CWebAPIValues *m_pValues;
|
||||
EHTTPStatusCode m_eStatusCode;
|
||||
uint32 m_unExpirationSeconds;
|
||||
RTime32 m_rtLastModified;
|
||||
bool m_bExtendedArrays;
|
||||
};
|
||||
|
||||
class CWebAPIValues
|
||||
{
|
||||
public:
|
||||
CWebAPIValues( CWebAPIValues *pParent, const char *pchName, EWebAPIValueType eValueType, const char *pchArrayElementNames = NULL );
|
||||
|
||||
~CWebAPIValues();
|
||||
|
||||
//
|
||||
// Child node handling
|
||||
//
|
||||
|
||||
// Create a child object of this node, all children of the resultant
|
||||
// object must be named.
|
||||
CWebAPIValues *CreateChildObject( const char *pchName );
|
||||
|
||||
// Add a child object to the array, this should only be called on objects that are of the array type
|
||||
CWebAPIValues *AddChildObjectToArray();
|
||||
|
||||
// Add a child array to the array, this should only be called on objects that are of the array type
|
||||
CWebAPIValues *AddChildArrayToArray( const char * pchArrayElementNames );
|
||||
|
||||
// Create a child array of this node. Note that array nodes can only
|
||||
// have un-named children, in XML the pchArrayElementNames value will be used
|
||||
// as the element name for each of the children of the array, in JSON it will simply
|
||||
// be a numerically indexed [] array.
|
||||
CWebAPIValues *CreateChildArray( const char *pchName, const char *pchArrayElementNames );
|
||||
|
||||
// Find first matching child by name, O(N) on number of children, this class isn't designed for searching
|
||||
CWebAPIValues * FindChild( const char *pchName );
|
||||
const CWebAPIValues * FindChild( const char *pchName ) const { return const_cast<CWebAPIValues *>(this)->FindChild( pchName ); }
|
||||
|
||||
// Get the first child of this node
|
||||
CWebAPIValues * GetFirstChild();
|
||||
const CWebAPIValues * GetFirstChild() const { return m_pFirstChild; }
|
||||
|
||||
// Call this on the returned value from GetFirstChild() or a previous GetNextChild() call to
|
||||
// proceed to the next child of the parent GetFirstChild() was originally called on.
|
||||
CWebAPIValues * GetNextChild();
|
||||
const CWebAPIValues * GetNextChild() const { return m_pNextPeer; }
|
||||
|
||||
// Returns the parent of this node or NULL if this is the root of a tree
|
||||
CWebAPIValues * GetParent();
|
||||
const CWebAPIValues * GetParent() const { return m_pParent; }
|
||||
|
||||
// Deletes the child with the given name - no-op if no child by that name
|
||||
void DeleteChild( const char *pchName );
|
||||
|
||||
//
|
||||
// Setters
|
||||
//
|
||||
|
||||
// Set string value
|
||||
void SetStringValue( const char *pchValue );
|
||||
|
||||
// Set int32 value
|
||||
void SetInt32Value( int32 nValue );
|
||||
|
||||
// Set uint32 value
|
||||
void SetUInt32Value( uint32 unValue );
|
||||
|
||||
// Set int64 value
|
||||
void SetInt64Value ( int64 lValue );
|
||||
|
||||
// Set uint64 value
|
||||
void SetUInt64Value( uint64 ulValue );
|
||||
|
||||
// Set double value
|
||||
void SetDoubleValue( double flValue );
|
||||
|
||||
// Set binary blob value
|
||||
void SetBinaryValue( uint8 *pValue, uint32 unBytes );
|
||||
|
||||
// Set boolean value
|
||||
void SetBoolValue( bool bValue );
|
||||
|
||||
// Set boolean value
|
||||
void SetNullValue( );
|
||||
|
||||
//
|
||||
// Accessors
|
||||
//
|
||||
|
||||
// Get the name of the current node
|
||||
const char *GetName() const { return m_pchName; }
|
||||
|
||||
// get the name of the elements of this numeric array (if this is an array)
|
||||
const char *GetElementName() const { return m_pchArrayChildElementName; }
|
||||
|
||||
// Get the type currently held by the node
|
||||
EWebAPIValueType GetType() const;
|
||||
|
||||
// returns true if this is an object
|
||||
bool IsObject() const { return GetType() == k_EWebAPIValueType_Object; }
|
||||
|
||||
// returns true if this is an object
|
||||
bool IsArray() const { return GetType() == k_EWebAPIValueType_NumericArray; }
|
||||
|
||||
// Get int32 value
|
||||
int32 GetInt32Value() const;
|
||||
|
||||
// Get uint32 value
|
||||
uint32 GetUInt32Value() const;
|
||||
|
||||
// Get int64 value
|
||||
int64 GetInt64Value() const;
|
||||
|
||||
// Get uint64 value
|
||||
uint64 GetUInt64Value() const;
|
||||
|
||||
// Get double value
|
||||
double GetDoubleValue() const;
|
||||
|
||||
// Get string value
|
||||
void GetStringValue( CUtlString &stringOut ) const;
|
||||
|
||||
// Get binary blob value
|
||||
void GetBinaryValue( CUtlBuffer &bufferOut ) const;
|
||||
|
||||
// Get bool value
|
||||
bool GetBoolValue() const;
|
||||
|
||||
// Get Null value
|
||||
bool IsNullValue() const { return GetType() == k_EWebAPIValueType_Null; }
|
||||
|
||||
//
|
||||
// Child Setters
|
||||
//
|
||||
|
||||
// Set string value
|
||||
void SetChildStringValue( const char *pchChildName, const char *pchValue );
|
||||
|
||||
// Set int32 value
|
||||
void SetChildInt32Value( const char *pchChildName, int32 nValue );
|
||||
|
||||
// Set uint32 value
|
||||
void SetChildUInt32Value( const char *pchChildName, uint32 unValue );
|
||||
|
||||
// Set int64 value
|
||||
void SetChildInt64Value ( const char *pchChildName, int64 lValue );
|
||||
|
||||
// Set uint64 value
|
||||
void SetChildUInt64Value( const char *pchChildName, uint64 ulValue );
|
||||
|
||||
// Set double value
|
||||
void SetChildDoubleValue( const char *pchChildName, double flValue );
|
||||
|
||||
// Set binary blob value
|
||||
void SetChildBinaryValue( const char *pchChildName, uint8 *pValue, uint32 unBytes );
|
||||
|
||||
// Set boolean value
|
||||
void SetChildBoolValue( const char *pchChildName, bool bValue );
|
||||
|
||||
// Set null value
|
||||
void SetChildNullValue( const char *pchChildName );
|
||||
|
||||
//
|
||||
// Accessors
|
||||
//
|
||||
|
||||
// Get int32 value
|
||||
int32 GetChildInt32Value( const char *pchChildName, int32 nDefault = 0 ) const;
|
||||
|
||||
// Get uint32 value
|
||||
uint32 GetChildUInt32Value( const char *pchChildName, uint32 unDefault = 0 ) const;
|
||||
|
||||
// Get int64 value
|
||||
int64 GetChildInt64Value( const char *pchChildName, int64 lDefault = 0 ) const;
|
||||
|
||||
// Get uint64 value
|
||||
uint64 GetChildUInt64Value( const char *pchChildName, uint64 ulDefault = 0 ) const;
|
||||
|
||||
// Get double value
|
||||
double GetChildDoubleValue( const char *pchChildName, double flDefault = 0 ) const;
|
||||
|
||||
// Get string value
|
||||
void GetChildStringValue( CUtlString &stringOut, const char *pchChildName, const char *pchDefault ) const;
|
||||
|
||||
// Get binary blob value (returns false if the child wasn't found)
|
||||
bool BGetChildBinaryValue( CUtlBuffer &bufferOut, const char *pchChildName ) const;
|
||||
|
||||
// Get bool value
|
||||
bool GetChildBoolValue( const char *pchChildName, bool bDefault = false ) const;
|
||||
|
||||
// get null value
|
||||
bool IsChildNullValue( const char *pchChildName ) const;
|
||||
|
||||
//
|
||||
// Output methods
|
||||
//
|
||||
|
||||
// Emits JSON formatted representation of response
|
||||
static bool BEmitJSONRecursive( CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, size_t unMaxResultSize, bool bIncludeArrayElementName = true );
|
||||
|
||||
// Emits KeyValues .vdf style formatted representation of response
|
||||
static bool BEmitVDFRecursive( CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, uint32 nArrayElement, size_t unMaxResultSize, bool bIncludeArrayElementName = true );
|
||||
|
||||
// Emits XML formatted representation of response
|
||||
static bool BEmitXMLRecursive( CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer,int nTabLevel, size_t unMaxResultSize );
|
||||
|
||||
//
|
||||
// Parsing methods
|
||||
//
|
||||
|
||||
// parses JSON into a tree of CWebAPIValues nodes.
|
||||
static CWebAPIValues * ParseJSON( CUtlBuffer &inputBuffer );
|
||||
static CWebAPIValues * ParseJSON( const char *pchJSONString );
|
||||
|
||||
//
|
||||
// Utility methods
|
||||
//
|
||||
|
||||
// copies the children and type from the specified node into this node
|
||||
void CopyFrom( const CWebAPIValues *pSource );
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName, bool bTopLevelNode = true, bool bValidatePeers = true );
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
private:
|
||||
|
||||
// sets the name of the node when constructing or copying
|
||||
void SetName( const char * pchName );
|
||||
|
||||
// Clears any existing value, freeing memory if needed
|
||||
void ClearValue();
|
||||
|
||||
// Assert that we don't have any child nodes, this is used when setting a native type value. We don't
|
||||
// support having both our own value and children. You are either an array of more values, or you are a value yourself.
|
||||
void AssertNoChildren();
|
||||
|
||||
// Internal helper for creating children
|
||||
CWebAPIValues *CreateChildInternal( const char *pchName, EWebAPIValueType eValueType, const char *pchArrayElementNames = NULL );
|
||||
|
||||
// Name of this node
|
||||
char *m_pchName;
|
||||
|
||||
// Data value contained in this node
|
||||
EWebAPIValueType m_eValueType;
|
||||
|
||||
struct WebAPIBinaryValue_t
|
||||
{
|
||||
uint8 *m_pData;
|
||||
uint32 m_unBytes;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
int32 m_nValue;
|
||||
int64 m_lValue;
|
||||
uint32 m_unValue;
|
||||
uint64 m_ulValue;
|
||||
double m_flValue;
|
||||
char *m_pchValue;
|
||||
bool m_bValue;
|
||||
char *m_pchArrayChildElementName;
|
||||
WebAPIBinaryValue_t m_BinaryValue;
|
||||
};
|
||||
|
||||
CWebAPIValues * m_pFirstChild;
|
||||
CWebAPIValues * m_pLastChild;
|
||||
CWebAPIValues * m_pNextPeer;
|
||||
CWebAPIValues * m_pParent;
|
||||
};
|
||||
|
||||
#define FOR_EACH_WEBAPI_CHILD( pParentParam, pChildParam ) \
|
||||
for( CWebAPIValues *pChildParam = pParentParam->GetFirstChild(); pChildParam != NULL; pChildParam = pChildParam->GetNextChild() )
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: KeyValues wrapper that automatically deletes itself on close
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWebAPIValuesAD
|
||||
{
|
||||
public:
|
||||
CWebAPIValuesAD()
|
||||
{
|
||||
m_pwav = NULL;
|
||||
}
|
||||
|
||||
// create a webapivalues object of the object type
|
||||
CWebAPIValuesAD( const char *pchName )
|
||||
{
|
||||
m_pwav = new CWebAPIValues( NULL, pchName, k_EWebAPIValueType_Object );
|
||||
}
|
||||
|
||||
// create a webapivalues object of the array type
|
||||
CWebAPIValuesAD( const char *pchName, const char *pchArrayElementName )
|
||||
{
|
||||
m_pwav = new CWebAPIValues( NULL, pchName, k_EWebAPIValueType_NumericArray, pchArrayElementName );
|
||||
}
|
||||
|
||||
CWebAPIValuesAD( const CWebAPIValuesAD &rhs )
|
||||
{
|
||||
m_pwav = NULL;
|
||||
Copy( rhs.m_pwav );
|
||||
}
|
||||
|
||||
CWebAPIValuesAD( const CWebAPIValues *pwav )
|
||||
{
|
||||
m_pwav = NULL;
|
||||
Copy( pwav );
|
||||
}
|
||||
|
||||
~CWebAPIValuesAD()
|
||||
{
|
||||
delete m_pwav;
|
||||
}
|
||||
|
||||
CWebAPIValues *operator->() { if ( !m_pwav ) m_pwav = new CWebAPIValues( NULL, "root", k_EWebAPIValueType_Object ); return m_pwav; }
|
||||
operator CWebAPIValues *() { if ( !m_pwav ) m_pwav = new CWebAPIValues( NULL, "root", k_EWebAPIValueType_Object ); return m_pwav; }
|
||||
operator const CWebAPIValues *() const { return m_pwav; }
|
||||
|
||||
CWebAPIValuesAD & operator= ( const CWebAPIValuesAD &rhs )
|
||||
{
|
||||
Copy( rhs.m_pwav );
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Take( CWebAPIValues *pwav )
|
||||
{
|
||||
if ( pwav )
|
||||
{
|
||||
delete m_pwav;
|
||||
m_pwav = pwav;
|
||||
}
|
||||
else if ( m_pwav )
|
||||
{
|
||||
delete m_pwav;
|
||||
m_pwav = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Copy( const CWebAPIValues *pwav )
|
||||
{
|
||||
if ( m_pwav )
|
||||
delete m_pwav;
|
||||
|
||||
if ( pwav )
|
||||
{
|
||||
if( pwav->IsArray() )
|
||||
m_pwav = new CWebAPIValues( NULL, pwav->GetName(), k_EWebAPIValueType_NumericArray, pwav->GetElementName() );
|
||||
else
|
||||
m_pwav = new CWebAPIValues( NULL, pwav->GetName(), k_EWebAPIValueType_Object );
|
||||
m_pwav->CopyFrom( pwav );
|
||||
}
|
||||
else
|
||||
m_pwav = NULL;
|
||||
|
||||
}
|
||||
|
||||
#ifdef DBGFLAG_VALIDATE
|
||||
void Validate( CValidator &validator, const char *pchName, bool bTopLevelNode = true, bool bValidatePeers = true )
|
||||
{
|
||||
ValidatePtr( m_pwav );
|
||||
}
|
||||
#endif // DBGFLAG_VALIDATE
|
||||
|
||||
private:
|
||||
CWebAPIValues *operator=(CWebAPIValues *); // use Take() or Copy()
|
||||
CWebAPIValues *m_pwav;
|
||||
};
|
||||
|
||||
// use to decode binary values
|
||||
uint32 Base64Decode( const uint8 *pubData, uint32 cubData, uint8 *pubDest, uint32 cubDest );
|
||||
bool Base64Encode( uint8 *pubData, uint32 cubData, uint8 *pubDest, uint32 cubDest );
|
||||
|
||||
}
|
||||
|
||||
#include "tier0/memdbgoff.h"
|
||||
|
||||
#endif // WEBAPI_RESPONSE_H
|
||||
369
public/gcsdk/workthreadpool.h
Normal file
369
public/gcsdk/workthreadpool.h
Normal file
@@ -0,0 +1,369 @@
|
||||
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
||||
//
|
||||
// Purpose: A thread pool implementation. You give it CWorkItems,
|
||||
// it processes them asynchronously, and hands them back to you when they've
|
||||
// been completed.
|
||||
//
|
||||
// To declare a queue, provide the implementation of a CWorkItem subtype,
|
||||
// the thread name prefix for threads in the pool, and the number of work
|
||||
// threads you want.
|
||||
//
|
||||
// CNet uses this class to offload encryption to a separate thread,
|
||||
// so that's a good place to start looking for usage examples.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef WORKTHREADPOOL_H
|
||||
#define WORKTHREADPOOL_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <refcount.h>
|
||||
#include <reliabletimer.h>
|
||||
#include "jobtime.h"
|
||||
|
||||
// forward declaration for CTSQueue which we can't statically allocate as our member
|
||||
// because of alignment issues on Win64
|
||||
template <class T, bool bTestOptimizer >
|
||||
class CTSQueue;
|
||||
|
||||
namespace GCSDK {
|
||||
|
||||
// forward declarations
|
||||
class CWorkThread;
|
||||
class CJobMgr;
|
||||
|
||||
|
||||
// these functions return pointers to fixed string in the code section. We need this for VPROF nodes
|
||||
#define DECLARE_WORK_ITEM( classname ) \
|
||||
virtual const char* GetDispatchCompletedName() const { return #classname"::DispatchCompleted"; } \
|
||||
virtual const char* GetThreadProcessName() const { return #classname"::ThreadProcess"; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Work item base class. Derive from this for specific work item types.
|
||||
// The derived type ideally should be self-contained with all data it
|
||||
// needs to perform the work.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWorkItem : public CRefCount
|
||||
{
|
||||
public:
|
||||
CWorkItem()
|
||||
: m_JobID( k_GIDNil ),
|
||||
m_bRunning( false ),
|
||||
m_bResubmit( false ),
|
||||
m_bCanceled( false ),
|
||||
m_ulSequenceNumber( 0 )
|
||||
{
|
||||
m_jobtimeTimeout.SetLTime( 0 );
|
||||
m_jobtimeQueued.SetToJobTime();
|
||||
}
|
||||
|
||||
CWorkItem( JobID_t jobID )
|
||||
: m_JobID( jobID ),
|
||||
m_bRunning( false ),
|
||||
m_bResubmit( false ),
|
||||
m_bCanceled( false ),
|
||||
m_ulSequenceNumber( 0 )
|
||||
{
|
||||
m_jobtimeTimeout.SetLTime( 0 );
|
||||
m_jobtimeQueued.SetToJobTime();
|
||||
}
|
||||
|
||||
CWorkItem( JobID_t jobID, int64 cTimeoutMicroseconds )
|
||||
: m_JobID( jobID ),
|
||||
m_bRunning( false ),
|
||||
m_bResubmit( false ),
|
||||
m_bCanceled( false ),
|
||||
m_ulSequenceNumber( 0 )
|
||||
{
|
||||
SetPreExecuteTimeout( cTimeoutMicroseconds );
|
||||
m_jobtimeQueued.SetToJobTime();
|
||||
}
|
||||
|
||||
void SetJobID( JobID_t jobID )
|
||||
{
|
||||
Assert(jobID != k_GIDNil) ;
|
||||
m_JobID = jobID;
|
||||
}
|
||||
JobID_t GetJobID() const { return m_JobID; }
|
||||
|
||||
bool HasTimedOut() const { return m_jobtimeTimeout.LTime() != 0 && m_jobtimeTimeout.CServerMicroSecsPassed() > 0; }
|
||||
int64 WaitingTime() const { return m_jobtimeQueued.CServerMicroSecsPassed(); }
|
||||
void SetPreExecuteTimeout( int64 cMicroSeconds ) { m_jobtimeTimeout.SetFromJobTime( cMicroSeconds ); }
|
||||
bool BPreExecuteTimeoutSet( ) const { return m_jobtimeTimeout.LTime() != 0; }
|
||||
void ForceTimeOut() { m_jobtimeTimeout.SetFromJobTime( -1 );}
|
||||
bool BIsRunning() const { return m_bRunning; } // true if running right now
|
||||
bool WasCancelled() const { return m_bCanceled; }
|
||||
void SetCycleCount( CCycleCount& cycleCount ) { m_CycleCount = cycleCount ; }
|
||||
CCycleCount GetCycleCount() { return m_CycleCount; }
|
||||
uint64 GetSequenceNumber() { return m_ulSequenceNumber; }
|
||||
|
||||
// Work threads can call this to force a work item to be reprocessed (added to the end of the process queue)
|
||||
void SetResubmit( bool bResubmit ) { m_bResubmit = bResubmit; }
|
||||
|
||||
// these functions return pointers to fixed string in the code section.
|
||||
// We need this for VPROF nodes, you must use the DECLARE_WORK_ITEM macro
|
||||
virtual const char* GetDispatchCompletedName() const = 0;
|
||||
virtual const char* GetThreadProcessName() const = 0;
|
||||
|
||||
// Return false if your operation failed in some way that you would want to know about
|
||||
// The CWorkThreadPool will count the failures.
|
||||
virtual bool ThreadProcess( CWorkThread *pThread ) = 0; // called by the worker thread
|
||||
virtual bool DispatchCompletedWorkItem( CJobMgr *jobMgr ); // called by main loop after item completed
|
||||
|
||||
protected:
|
||||
// note: destructor is private. This is a ref-counted object, private destructor ensures callers can't accidentally delete
|
||||
// directly, or declare on stack
|
||||
virtual ~CWorkItem() { }
|
||||
|
||||
friend class CWorkThread;
|
||||
friend class CWorkThreadPool;
|
||||
uint64 m_ulSequenceNumber; // Sequence number for the work item, used when enforcing output ordering as matching input order
|
||||
CCycleCount m_CycleCount; // A record of how long it took to execute this particular work item !
|
||||
|
||||
private:
|
||||
bool m_bResubmit; // true if the item should be resubmitted after last run
|
||||
volatile bool m_bRunning; // true if the work item is running right now
|
||||
bool m_bCanceled; // true if the work was canceled due to timeout
|
||||
CJobTime m_jobtimeTimeout; // time at which this result is no longer valid, so it shouldn't start to be processed
|
||||
CJobTime m_jobtimeQueued;
|
||||
JobID_t m_JobID;
|
||||
};
|
||||
|
||||
// forward decl
|
||||
class CWorkThreadPool;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Generic work thread implementation, to be specialized if necessary
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWorkThread : public CThread
|
||||
{
|
||||
public:
|
||||
CWorkThread( CWorkThreadPool *pThreadPool );
|
||||
CWorkThread( CWorkThreadPool *pThreadPool, const char *pszName );
|
||||
|
||||
virtual ~CWorkThread()
|
||||
{
|
||||
}
|
||||
|
||||
virtual int Run();
|
||||
|
||||
virtual void Cancel()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
CWorkThreadPool *m_pThreadPool; // parent pool
|
||||
volatile bool m_bExitThread; // set by CWorkThreadPool::StopWorkerThreads and possibly by subclasses of CWorkThread
|
||||
volatile bool m_bFinished; // set by CWorkThread::Run [note: must still check IsThreadRunning, and/or call Join]
|
||||
virtual void OnStart() { }
|
||||
virtual void OnExit() { }
|
||||
|
||||
friend class CWorkThreadPool;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// callback class to create work threads
|
||||
//-----------------------------------------------------------------------------
|
||||
class IWorkThreadFactory
|
||||
{
|
||||
public:
|
||||
virtual CWorkThread *CreateWorkerThread( class CWorkThreadPool *pWorkThreadPool ) = 0;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// reusable trivial implementation of IWorkThreadFactory
|
||||
//-----------------------------------------------------------------------------
|
||||
template<class T>
|
||||
class CWorkThreadFactory : public IWorkThreadFactory
|
||||
{
|
||||
public:
|
||||
virtual CWorkThread *CreateWorkerThread( class CWorkThreadPool *pWorkThreadPool )
|
||||
{
|
||||
return new T( pWorkThreadPool );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: interface class for object that the WorkThreadPool can signal when
|
||||
// there are completed work items to process
|
||||
//-----------------------------------------------------------------------------
|
||||
class IWorkThreadPoolSignal
|
||||
{
|
||||
public:
|
||||
virtual void Signal() = 0;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: pool of work threads.
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWorkThreadPool
|
||||
{
|
||||
friend class CWorkThread;
|
||||
public:
|
||||
|
||||
static void SetWorkItemCompletedSignal( IWorkThreadPoolSignal *pObject )
|
||||
{
|
||||
sm_pWorkItemsCompletedSignal = pObject;
|
||||
}
|
||||
|
||||
|
||||
CWorkThreadPool( const char *pszThreadNamePfx );
|
||||
|
||||
// eventually it might be nice to be able to resize these pools via console command
|
||||
// in that case, we'd want a constructor like this, and a PoolSize accessor/mutator pair
|
||||
// it makes this class much more complicated, however (growing the pool is easy, shrinking it
|
||||
// is less easy) so we'll punt for now.
|
||||
/* CWorkThreadPool( const char *pszName = "unnamed thread" ) : CWorkThreadPool( pszName, -1 ); */
|
||||
|
||||
virtual ~CWorkThreadPool();
|
||||
|
||||
// Setting this will ensure that items of the same priority complete and get dispatched in the same order
|
||||
// they are added to the threadpool. This has a small additional locking overhead and can increase latency
|
||||
// as items that are actually completed out-of-order have to queue waiting on earlier items.
|
||||
void SetEnsureOutputOrdering( bool bEnsureOutputOrdering ) { m_bEnsureOutputOrdering = bEnsureOutputOrdering; }
|
||||
|
||||
void AllowTimeouts( bool bMayHaveJobTimeouts ) { m_bMayHaveJobTimeouts = bMayHaveJobTimeouts; }
|
||||
|
||||
int AddWorkThread( CWorkThread *pThread );
|
||||
void StartWorkThreads(); // gentlemen, start your engines
|
||||
void StopWorkThreads(); // stop work threads
|
||||
bool HasWorkItemsToProcess() const;
|
||||
|
||||
// sets it to use dynamic worker thread construction
|
||||
// if pWorkThreadControl is NULL, just creates a standard CWorkThread object
|
||||
void SetWorkThreadAutoConstruct( int cMaxThreads, IWorkThreadFactory *pWorkThreadConstructor );
|
||||
|
||||
bool AddWorkItem( CWorkItem *pWorkItem ); // add a work item to the queue to process
|
||||
CWorkItem *GetNextCompletedWorkItem( ); // get next completed work item and it's priority if needed
|
||||
const char *GetThreadNamePrefix() const { return m_szThreadNamePfx; }
|
||||
|
||||
void SetNeverSetEventOnAdd( bool bNeverSet );
|
||||
bool BNeverSetEventOnAdd() { return m_bNeverSetOnAdd; }
|
||||
|
||||
// get count of completed work items
|
||||
// can't be inline because of m_TSQueueCompleted type
|
||||
int GetCompletedWorkItemCount() const;
|
||||
|
||||
// get count of work items to process
|
||||
// can't be inline because of m_TSQueueToProcess type
|
||||
int GetWorkItemToProcessCount() const;
|
||||
|
||||
uint64 GetLastUsedSequenceNumber( ) const
|
||||
{
|
||||
return m_ulLastUsedSequenceNumber;
|
||||
}
|
||||
|
||||
uint64 GetLastCompletedSequenceNumber( ) const
|
||||
{
|
||||
return m_ulLastCompletedSequenceNumber;
|
||||
}
|
||||
|
||||
uint64 GetLastDispatchedSequenceNumber( ) const
|
||||
{
|
||||
return m_ulLastDispatchedSequenceNumber;
|
||||
}
|
||||
|
||||
#if 0
|
||||
uint64 GetAveExecutionTime() const
|
||||
{
|
||||
return m_StatExecutionTime.GetUlAvg();
|
||||
}
|
||||
uint64 GetAveWaitTime() const
|
||||
{
|
||||
return m_StatWaitTime.GetUlAvg();
|
||||
}
|
||||
uint64 GetCurrentBacklogTime() const;
|
||||
#endif
|
||||
|
||||
int CountCompletedSuccess() const { return m_cSuccesses; }
|
||||
int CountRetries() const { return m_cRetries; }
|
||||
int CountCompletedFailed() const { return m_cFailures; }
|
||||
|
||||
bool BDispatchCompletedWorkItems( CLimitTimer &limitTimer, CJobMgr *pJobMgr );
|
||||
bool BExiting() const { return m_bExiting; }
|
||||
|
||||
int GetWorkerCount() const { return m_WorkThreads.Count(); }
|
||||
|
||||
uint GetActiveThreadCount() const { return m_cActiveThreads; }
|
||||
|
||||
// make sure you lock before using this
|
||||
const CWorkThread *GetWorkThread( int iIndex ) const
|
||||
{
|
||||
Assert( iIndex >= 0 && iIndex < m_WorkThreads.Count() );
|
||||
return m_WorkThreads[iIndex];
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// STATICS
|
||||
static IWorkThreadPoolSignal *sm_pWorkItemsCompletedSignal;
|
||||
|
||||
// MEMBERS
|
||||
CWorkItem *GetNextWorkItemToProcess( );
|
||||
void StartWorkThread( CWorkThread *pWorkThread, int iName );
|
||||
|
||||
// meaningful thread name prefix
|
||||
char m_szThreadNamePfx[32];
|
||||
// have we actually initialized the threadpool?
|
||||
bool m_bThreadsInitialized;
|
||||
|
||||
// Incoming queue: queue of all work items to process
|
||||
// must be dynamically allocated for alignment requirements on Win64
|
||||
CTSQueue< CWorkItem *, false > *m_pTSQueueToProcess;
|
||||
|
||||
// Outgoing queues: queue of all completed work items
|
||||
// must be dynamically allocated for alignment requirements on Win64
|
||||
CTSQueue< CWorkItem *, false > *m_pTSQueueCompleted;
|
||||
|
||||
// Vectors of completed, but out of order and waiting work items, only used when bEnsureOutputOrdering == true
|
||||
CThreadMutex m_MutexOnItemCompletedOrdered;
|
||||
CUtlVector< CWorkItem * > m_vecCompletedAndWaiting;
|
||||
|
||||
// Should we emit work items in the same order they are received (on a per priority basis)
|
||||
bool m_bEnsureOutputOrdering;
|
||||
|
||||
// Sequence numbers
|
||||
uint64 m_ulLastUsedSequenceNumber;
|
||||
uint64 m_ulLastCompletedSequenceNumber;
|
||||
uint64 m_ulLastDispatchedSequenceNumber;
|
||||
|
||||
bool m_bMayHaveJobTimeouts;
|
||||
CUtlVector< CWorkThread * > m_WorkThreads;
|
||||
CThreadMutex m_WorkThreadMutex;
|
||||
CInterlockedUInt m_cThreadsRunning; // how many threads are running
|
||||
volatile bool m_bExiting; // are we exiting
|
||||
CThreadEvent m_EventNewWorkItem; // event set when a new work item is available to process
|
||||
CInterlockedInt m_cActiveThreads;
|
||||
volatile bool m_bNeverSetOnAdd;
|
||||
|
||||
bool m_bAutoCreateThreads;
|
||||
int m_cMaxThreads;
|
||||
IWorkThreadFactory *m_pWorkThreadConstructor;
|
||||
|
||||
// override this method if you want to do any special handling of completed work items. Default implementation puts
|
||||
// work items in our completed item queue.
|
||||
virtual void OnWorkItemCompleted( CWorkItem *pWorkItem );
|
||||
|
||||
bool BTryDeleteExitedWorkerThreads();
|
||||
|
||||
int m_cSuccesses;
|
||||
int m_cFailures;
|
||||
int m_cRetries;
|
||||
#if 0
|
||||
CStat m_StatExecutionTime;
|
||||
CStat m_StatWaitTime;
|
||||
#endif
|
||||
CLimitTimer m_LimitTimerCreateNewThreads;
|
||||
};
|
||||
|
||||
} // namespace GCSDK
|
||||
|
||||
|
||||
#endif // WORKTHREAD_H
|
||||
Reference in New Issue
Block a user