1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for objects that are kept in synch between client and server
//
//=============================================================================
#ifndef SHAREDOBJECT_H
#define SHAREDOBJECT_H
#ifdef _WIN32
#pragma once
#endif
// ENABLE_SO_OVERWRITE_PARANOIA can be set to either 0 or 1. If enabled, it will add
// extra fields to every CSharedObject instance to try and detect overwrites at the
// cost of additional runtime memory.
#define ENABLE_SO_OVERWRITE_PARANOIA 0
// ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA can be set to either 0 or 1. If enabled, it
// will add extra fields to every CSharedObject instance to try and detect issues with
// constructions/destruction (ie., double-deletes, etc.), including reference counting.
#define ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA (defined( STAGING_ONLY ))
#include "tier0/memdbgon.h"
namespace GCSDK
{
class CSQLAccess;
class CSharedObject;
typedef CSharedObject *(*SOCreationFunc_t)( );
class CSharedObjectCache;
//----------------------------------------------------------------------------
// Purpose: Abstract base class for objects that are shared between the GC and
// a gameserver/client. These can also be stored in the database.
//----------------------------------------------------------------------------
class CSharedObject
{
friend class CGCSharedObjectCache;
friend class CSharedObjectCache;
public:
#ifdef GC
virtual ~CSharedObject()
{
#if ENABLE_SO_OVERWRITE_PARANOIA
m_pThis = NULL;
#endif // ENABLE_SO_OVERWRITE_PARANOIA
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
AssertMsg2( m_nRefCount == 0, "Deleting shared object type %d with refcount %d", m_nSOTypeID, m_nRefCount );
m_nSOTypeID = -1;
m_nRefCount = -1;
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
}
#if ENABLE_SO_OVERWRITE_PARANOIA
CSharedObject *m_pThis;
#endif // ENABLE_SO_OVERWRITE_PARANOIA
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
int m_nRefCount;
mutable int m_nSOTypeID;
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
void Check() const
{
#if ENABLE_SO_OVERWRITE_PARANOIA
Assert( m_pThis == this );
#endif // ENABLE_SO_OVERWRITE_PARANOIA
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
Assert( m_nRefCount >= 0 );
if ( m_nSOTypeID == kSharedObject_UnassignedType )
{
m_nSOTypeID = GetTypeID();
}
Assert( m_nSOTypeID >= 0 );
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
}
#else
virtual ~CSharedObject() {}
#endif
virtual int GetTypeID() const = 0;
virtual bool BParseFromMessage( const CUtlBuffer & buffer ) = 0;
virtual bool BParseFromMessage( const std::string &buffer ) = 0;
virtual bool BUpdateFromNetwork( const CSharedObject & objUpdate ) = 0;
virtual bool BIsKeyLess( const CSharedObject & soRHS ) const = 0;
virtual void Copy( const CSharedObject & soRHS ) = 0;
virtual void Dump() const = 0;
virtual bool BShouldDeleteByCache() const { return true; }
virtual CUtlString GetDebugString() const { return PchClassName( GetTypeID() ); };
bool BIsKeyEqual( const CSharedObject & soRHS ) const;
static void RegisterFactory( int nTypeID, SOCreationFunc_t fnFactory, uint32 unFlags, const char *pchClassName );
static CSharedObject *Create( int nTypeID );
static uint32 GetTypeFlags( int nTypeID );
static const char *PchClassName( int nTypeID );
static const char *PchClassBuildCacheNodeName( int nTypeID );
static const char *PchClassCreateNodeName( int nTypeID );
static const char *PchClassUpdateNodeName( int nTypeID );
#ifdef GC
virtual bool BIsNetworked() const { return true; }
virtual bool BIsDatabaseBacked() const { return true; }
virtual bool BYieldingAddToDatabase();
virtual bool BYieldingWriteToDatabase( const CUtlVector< int > &fields );
virtual bool BYieldingRemoveFromDatabase();
virtual bool BYieldingAddInsertToTransaction( CSQLAccess & sqlAccess ) { return false; }
virtual bool BYieldingAddWriteToTransaction( CSQLAccess & sqlAccess, const CUtlVector< int > &fields ) { return false; }
virtual bool BYieldingAddRemoveToTransaction( CSQLAccess & sqlAccess ) { return false; }
virtual bool BAddToMessage( CUtlBuffer & bufOutput ) const = 0;
virtual bool BAddToMessage( std::string *pBuffer ) const = 0;
virtual bool BAddDestroyToMessage( CUtlBuffer & bufDestroy ) const = 0;
virtual bool BAddDestroyToMessage( std::string *pBuffer ) const = 0;
virtual bool BParseFromMemcached( CUtlBuffer & buffer ) { return false; }
virtual bool BAddToMemcached( CUtlBuffer & bufOutput ) const { return false; }
bool BSendCreateToSteamID( const CSteamID & steamID, const CSteamID & ownerID, uint64 ulVersion ) const;
bool BSendDestroyToSteamID( const CSteamID & steamID, const CSteamID & ownerID, uint64 ulVersion ) const;
#ifdef DBGFLAG_VALIDATE
virtual void Validate( CValidator &validator, const char *pchName );
static void ValidateStatics( CValidator & validator );
#endif
protected:
/*
// Dirty bit modification. Do not call these directly on SharedObjects. Call them
// on the cache that owns the object so they can be added/removed from the right lists.
virtual void DirtyField( int nField ) = 0;
virtual void MakeDatabaseClean() = 0;
virtual void MakeNetworkClean() = 0;
*/
#endif // GC
#ifdef GC
CSharedObject()
{
#if ENABLE_SO_OVERWRITE_PARANOIA
m_pThis = this;
#endif // ENABLE_SO_OVERWRITE_PARANOIA
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
m_nRefCount = 0;
m_nSOTypeID = kSharedObject_UnassignedType;
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
}
#endif
private:
#if ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
enum { kSharedObject_UnassignedType = -999 };
#endif // ENABLE_SO_CONSTRUCT_DESTRUCT_PARANOIA
struct SharedObjectInfo_t
{
SOCreationFunc_t m_pFactoryFunction;
uint32 m_unFlags;
const char *m_pchClassName;
CUtlString m_sBuildCacheSubNodeName;
CUtlString m_sUpdateNodeName;
CUtlString m_sCreateNodeName;
};
static CUtlMap<int, SharedObjectInfo_t> sm_mapFactories;
public:
static const CUtlMap<int, SharedObjectInfo_t> & GetFactories() { return sm_mapFactories; }
};
typedef CUtlVectorFixedGrowable<CSharedObject *, 1> CSharedObjectVec;
#ifdef GC
//this class manages the stats for the shared objects, such as how many exist, how many have been sent, how many have been created, how many destroyed
class CSharedObjectStats
{
public:
CSharedObjectStats();
//called to register a shared object class given a type ID and name. This will return false if
//a type of this ID is already registered
void RegisterSharedObjectType( int nTypeID, const char* pszTypeName );
//called to track when a shared object is created/destroyed to update the running total
void TrackSharedObjectLifetime( int nTypeID, int32 nCount );
//called when a shared object is created to track its creation
void TrackSharedObjectSendCreate( int nTypeID, uint32 nCount );
//called when a shared object is destroyed to track counts
void TrackSharedObjectSendDestroy( int nTypeID, uint32 nCount );
//tracks a new subscription
void TrackSubscription( int nTypeID, uint32 nFlags, uint32 nNumSubscriptions );
//called when a shared object is sent
void TrackSharedObjectSend( int nTypeID, uint32 nNumClients, uint32 nMsgSize );
//reset the stats for all the caches
void ResetStats();
//called to start/stop collecting stats
void StartCollectingStats();
void StopCollectingStats();
bool IsCollectingStats() const { return m_bCollectingStats; }
//called to report the currently collected stats
void ReportCollectedStats() const;
private:
struct SOStats_t
{
SOStats_t();
void ResetStats();
//the text name of this cache
CUtlString m_sName;
//the number of outstanding SO cache objects of this type. This is not cleared
uint32 m_nNumActive;
//the type ID of this stat
int m_nTypeID;
//the total number that have been created/destroyed. Active is difference between these
uint32 m_nNumCreated;
uint32 m_nNumDestroyed;
//how many bytes we have sent (raw, and multiplexed to multiple subscribers)
uint64 m_nRawBytesSent;
uint64 m_nMultiplexedBytesSent;
//the number of sends since our last clear
uint32 m_nNumSends;
//how many subscriptions we have gotten by various subscription types
uint32 m_nNumSubOwner;
uint32 m_nNumSubOtherUsers;
uint32 m_nNumSubGameServer;
};
//sort function that controls the order that the SO stats are presented in the report
static bool SortSOStatsSent( const SOStats_t* pLhs, const SOStats_t* pRhs );
static bool SortSOStatsSubscribe( const SOStats_t* pLhs, const SOStats_t* pRhs );
//to compact the memory space for wide ranging type IDs, the list of stats has two components, an array that can be looked up
//via the type ID which maps to a stat index (or invalid index if no mapping is registered)
static const uint16 knInvalidIndex = ( uint16 )-1;
CUtlVector< uint16 > m_vTypeToIndex;
CUtlVector< SOStats_t > m_Stats;
//are we currently collecting stats or not?
bool m_bCollectingStats;
//the time that we've been collecting
CJobTime m_CollectTime;
uint64 m_nMicroSElapsed;
};
//global stat tracker
extern CSharedObjectStats g_SharedObjectStats;
#endif
//----------------------------------------------------------------------------
// Purpose: Templatized function to use as a factory method for
// CSharedObject subclasses
//----------------------------------------------------------------------------
template<typename SharedObjectSubclass_t>
CSharedObject *CreateSharedObjectSubclass()
{
return new SharedObjectSubclass_t();
}
// Version that always asserts and returns NULL, for SOs that should not be auto-created this way
template<int nSharedObjectType>
CSharedObject *CreateSharedObjectSubclassProhibited()
{
AssertMsg( false, "Attempting to auto-create object of type %d which does not allow SO-based creation", nSharedObjectType );
return NULL;
}
#ifdef GC
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass, flags ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, (flags), #derivedClass )
// GC only -- sharedobjects that cannot be auto-created by the SO code, and might not have a specific derived class that
// publicly implements the interface
#define REG_SHARED_OBJECT_SUBCLASS_BY_ID_NOCREATE( strName, nTypeID, flags ) \
GCSDK::CSharedObject::RegisterFactory( nTypeID, CreateSharedObjectSubclassProhibited<nTypeID>, (flags), strName )
#else
#define REG_SHARED_OBJECT_SUBCLASS( derivedClass ) GCSDK::CSharedObject::RegisterFactory( derivedClass::k_nTypeID, GCSDK::CreateSharedObjectSubclass<derivedClass>, 0, #derivedClass )
#endif
} // namespace GCSDK
#include "tier0/memdbgoff.h"
#endif //SHAREDOBJECT_H
|