This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

View File

@@ -0,0 +1,28 @@
//-----------------------------------------------------------------------------
// gcsdk_include.vpc
//
// Project Script
//-----------------------------------------------------------------------------
$MacroRequired "PLATFORM"
$MacroRequired GENERATED_PROTO_DIR
$include "$SRCDIR\gcsdk\steammessages_include.vpc"
$MacroRequired PROTOBUF_BUILDER_INCLUDED
$Configuration
{
$Compiler
{
$AdditionalIncludeDirectories "$BASE;$SRCDIR\gcsdk\steamextra"
}
}
$Project
{
$Folder "Link Libraries"
{
$Lib gcsdk
}
}

View File

@@ -0,0 +1,74 @@
//======= Copyright © Valve Corporation, All rights reserved. =================
//
// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose
// hash with a 32-bit output. References:
// http://code.google.com/p/smhasher/ (home of MurmurHash3)
// https://sites.google.com/site/murmurhash/avalanche
// http://www.strchr.com/hash_functions
//
//=============================================================================
#include <stdafx.h>
#include "murmurhash3.h"
//-----------------------------------------------------------------------------
uint32 MurmurHash3_32( const void * key, size_t len, uint32 seed, bool bCaselessStringVariant )
{
const uint8 * data = (const uint8*)key;
const ptrdiff_t nblocks = len / 4;
uint32 uSourceBitwiseAndMask = 0xDFDFDFDF | ((uint32)bCaselessStringVariant - 1);
uint32 h1 = seed;
//----------
// body
const uint32 * blocks = (const uint32 *)(data + nblocks*4);
for(ptrdiff_t i = -nblocks; i; i++)
{
uint32 k1 = LittleDWord(blocks[i]);
k1 &= uSourceBitwiseAndMask;
k1 *= 0xcc9e2d51;
k1 = (k1 << 15) | (k1 >> 17);
k1 *= 0x1b873593;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >> 19);
h1 = h1*5+0xe6546b64;
}
//----------
// tail
const uint8 * tail = (const uint8*)(data + nblocks*4);
uint32 k1 = 0;
switch(len & 3)
{
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 &= uSourceBitwiseAndMask;
k1 *= 0xcc9e2d51;
k1 = (k1 << 15) | (k1 >> 17);
k1 *= 0x1b873593;
h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
return h1;
}

View File

@@ -0,0 +1,100 @@
//======= Copyright © Valve Corporation, All rights reserved. =================
//
// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose
// hash with a 32-bit output. References:
// http://code.google.com/p/smhasher/ (home of MurmurHash3)
// https://sites.google.com/site/murmurhash/avalanche
// http://www.strchr.com/hash_functions
//
//=============================================================================
#ifndef MURMURHASH3_H
#define MURMURHASH3_H
#if defined(_WIN32)
#pragma once
#endif
uint32 MurmurHash3_32( const void *key, size_t len, uint32 seed, bool bCaselessStringVariant = false );
inline uint32 MurmurHash3String( const char *pszKey, size_t len )
{
return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, false );
}
inline uint32 MurmurHash3StringCaseless( const char *pszKey, size_t len )
{
return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, true );
}
inline uint32 MurmurHash3String( const char *pszKey )
{
return MurmurHash3String( pszKey, strlen( pszKey ) );
}
inline uint32 MurmurHash3StringCaseless( const char *pszKey )
{
return MurmurHash3StringCaseless( pszKey, strlen( pszKey ) );
}
template <typename T>
inline uint32 MurmurHash3Item( const T &item )
{
return MurmurHash3_32( &item, sizeof(item), 1047 );
}
inline uint32 MurmurHash3Int( uint32 h )
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
template <>
inline uint32 MurmurHash3Item( const uint32 &item )
{
return MurmurHash3Int( item );
}
template <>
inline uint32 MurmurHash3Item( const int32 &item )
{
return MurmurHash3Int( item );
}
template<typename T>
struct MurmurHash3Functor
{
typedef uint32 TargetType;
TargetType operator()(const T &key) const
{
return MurmurHash3Item( key );
}
};
template<>
struct MurmurHash3Functor<char *>
{
typedef uint32 TargetType;
TargetType operator()(const char *key) const
{
return MurmurHash3String( key );
}
};
template<>
struct MurmurHash3Functor<const char *>
{
typedef uint32 TargetType;
TargetType operator()(const char *key) const
{
return MurmurHash3String( key );
}
};
#endif // MURMURHASH3_H

View File

@@ -0,0 +1,234 @@
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//=============================================================================
//#include "pch_vstdlib.h"
#include "stdafx.h"
#include "tier0/tslist.h"
#include "tier0/t0constants.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const uint k_cubBytesAllocatedToConsiderFreeingMemory = 5 * k_nMegabyte;
static const int k_cBlocksAllocatedToConsiderFreeingMemory = 10;
typedef TSLNodeBase_t FreeListItem_t;
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CThreadSafeMemoryPool::CThreadSafeMemoryPool( int blockSize, int numElements, int growMode )
{
m_ptslistFreeBlocks = new CTSListBase;
// round up to the nearest 8-byte boundary
if ( blockSize % TSLIST_NODE_ALIGNMENT != 0 )
{
blockSize += TSLIST_NODE_ALIGNMENT - (blockSize % TSLIST_NODE_ALIGNMENT);
}
Assert( blockSize % TSLIST_NODE_ALIGNMENT == 0 );
Assert( blockSize > sizeof(FreeListItem_t) );
m_nGrowMode = growMode;
m_cubBlockSize = blockSize;
m_nGrowSize = numElements;
m_cubAllocated = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Frees the memory contained in the mempool
//-----------------------------------------------------------------------------
CThreadSafeMemoryPool::~CThreadSafeMemoryPool()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
FOR_EACH_VEC( m_vecBlockSets, i )
{
_aligned_free( m_vecBlockSets[i].m_pubBlockSet );
}
delete m_ptslistFreeBlocks;
}
//-----------------------------------------------------------------------------
// Purpose: Frees everything
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Clear()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
ClearNoLock();
}
//-----------------------------------------------------------------------------
// Purpose: Frees everything
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::ClearNoLock()
{
FOR_EACH_VEC( m_vecBlockSets, i )
{
_aligned_free( m_vecBlockSets[i].m_pubBlockSet );
}
m_ptslistFreeBlocks->Detach();
m_cubAllocated = 0;
m_cBlocksInUse = 0;
m_vecBlockSets.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a single block of memory from the pool.
//-----------------------------------------------------------------------------
void *CThreadSafeMemoryPool::Alloc()
{
return Alloc( m_cubBlockSize );
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a single block of memory from the pool.
//-----------------------------------------------------------------------------
void *CThreadSafeMemoryPool::Alloc( unsigned int amount )
{
// loop attempting to get memory
// there appears to be a case where memory corruption can get this into an infinite loop
// normally 1 or 2 attempts are necessary to get a block, so if we hit 1000 we know something is wrong
int cAttempts = 1000;
while ( --cAttempts )
{
// pull first from the free list
m_threadRWLock.LockForRead();
FreeListItem_t *pFreeListItem = m_ptslistFreeBlocks->Pop();
if ( pFreeListItem )
{
m_threadRWLock.UnlockRead();
m_cBlocksInUse++;
return (void *)pFreeListItem;
}
m_threadRWLock.UnlockRead();
// no free items, add a new block
// switch from a read lock to a write lock
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
// another thread may have allocated memory; try the free list again if so
if ( m_ptslistFreeBlocks->Count() > 0 )
continue;
size_t cubBlob = m_nGrowSize * m_cubBlockSize;
if ( m_nGrowMode == GROW_FAST )
{
cubBlob *= (m_vecBlockSets.Count() + 1);
}
// don't grow if we're told not to
if ( m_nGrowMode == GROW_NONE && m_vecBlockSets.Count() == 1 )
return NULL;
// allocate, but we can fail
byte *pBlobBase = (byte *)MemAlloc_AllocAligned( cubBlob, TSLIST_NODE_ALIGNMENT /*, (m_nGrowMode == GROW_TIL_YOU_CANT)*/ );
if ( !pBlobBase )
return NULL;
byte *pBlobEnd = pBlobBase + cubBlob;
// add all the items to the pool
for ( byte *pBlob = pBlobBase; pBlob < pBlobEnd; pBlob += m_cubBlockSize )
{
m_ptslistFreeBlocks->Push( (FreeListItem_t *)pBlob );
}
m_cubAllocated += cubBlob;
BlockSet_t blockSet = { pBlobBase, cubBlob };
m_vecBlockSets.AddToTail( blockSet );
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Frees a block of memory
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Free( void *pMem )
{
Free( pMem, m_cubBlockSize );
}
//-----------------------------------------------------------------------------
// Purpose: Frees a block of memory
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Free( void *pMem, int cubAlloc )
{
m_threadRWLock.LockForRead();
// push the item back onto the free list
m_ptslistFreeBlocks->Push( (FreeListItem_t *)pMem );
m_cBlocksInUse--;
m_threadRWLock.UnlockRead();
// if we're completely free, and have too much memory allocated, free some
if ( m_cBlocksInUse == 0
&& m_cubAllocated >= k_cubBytesAllocatedToConsiderFreeingMemory
&& m_vecBlockSets.Count() >= k_cBlocksAllocatedToConsiderFreeingMemory )
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
// check again nothing is in use
if ( m_cBlocksInUse == 0 )
{
// free all the blocks
ClearNoLock();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: display
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::PrintStats()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
int cBlocksInUse = m_cBlocksInUse;
Msg( "Block size: %-11s Alloc'd: %8d Num blobs: %5d (%s)\n", Q_pretifymem( m_cubBlockSize, 2, true ),
cBlocksInUse, m_vecBlockSets.Count(), Q_pretifymem( m_cubAllocated, 2, true ) );
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
size_t CThreadSafeMemoryPool::CubTotalSize()
{
return m_cubAllocated;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
size_t CThreadSafeMemoryPool::CubSizeInUse()
{
return m_cBlocksInUse * m_cubBlockSize;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
int CThreadSafeMemoryPool::Count()
{
return m_cBlocksInUse;
}

View File

@@ -0,0 +1,166 @@
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#ifndef TSMEMPOOL_H
#define TSMEMPOOL_H
#ifdef _WIN32
#pragma once
#endif
#undef new
//-----------------------------------------------------------------------------
// Purpose: Optimized pool memory allocator
//-----------------------------------------------------------------------------
class CThreadSafeMemoryPool
{
public:
enum
{
GROW_NONE=0, // Don't allow new blobs.
GROW_FAST=1, // New blob size is numElements * (i+1) (ie: the blocks it allocates get larger and larger each time it allocates one).
GROW_SLOW=2, // New blob size is numElements.
GROW_TIL_YOU_CANT=3 // GROW_SLOW til alloc fails - then STOP and dont assert!
};
CThreadSafeMemoryPool( int blockSize, int numElements, int growMode = GROW_FAST );
~CThreadSafeMemoryPool();
void *Alloc(); // Allocate the element size you specified in the constructor.
void *Alloc( unsigned int cubAlloc );
void Free( void *pMem );
void Free( void *pMem, int cubAlloc );
// Frees everything
void Clear();
// display
void PrintStats();
size_t CubTotalSize();
size_t CubSizeInUse();
int Count();
static void * operator new( size_t size )
{
CThreadSafeMemoryPool *pNode = (CThreadSafeMemoryPool *)MemAlloc_AllocAlignedFileLine( size, 8, __FILE__, __LINE__
#ifdef STEAM
, true // new operator
#endif
);
return pNode;
}
static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine )
{
CThreadSafeMemoryPool *pNode = (CThreadSafeMemoryPool *)MemAlloc_AllocAlignedFileLine( size, 8, pFileName, nLine
#ifdef STEAM
, true // new operator
#endif
);
return pNode;
}
static void operator delete( void *p)
{
MemAlloc_FreeAligned( p
#ifdef STEAM
, true // new operator
#endif
);
}
static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine )
{
MemAlloc_FreeAligned( p
#ifdef STEAM
, true // new operator
#endif
);
}
private:
// These ain't gonna work
static void * operator new[] ( size_t size );
static void operator delete [] ( void *p);
// CThreadSpinRWLock needs 8 byte alignment to work but we new() CThreadSafeMemoryPool
// so simply place it at the start of the class to make sure it fits on the 8-byte boundary
CThreadSpinRWLock m_threadRWLock;
int m_nGrowMode;
int m_cubBlockSize;
int m_nGrowSize;
void ClearNoLock();
CInterlockedInt m_cBlocksInUse;
size_t m_cubAllocated;
struct BlockSet_t
{
byte *m_pubBlockSet;
size_t m_cubAllocated;
};
CUtlVector<BlockSet_t> m_vecBlockSets;
class CTSListBase *m_ptslistFreeBlocks;
};
//-----------------------------------------------------------------------------
// Wrapper macro to make an allocator that returns particular typed allocations
// and construction and destruction of objects.
//-----------------------------------------------------------------------------
template< class T >
class CThreadSafeClassMemoryPool : public CThreadSafeMemoryPool
{
public:
CThreadSafeClassMemoryPool(int numElements, int growMode = GROW_FAST) :
CThreadSafeMemoryPool( sizeof(T), numElements, growMode ) {}
T* Alloc();
void Free( T *pMem );
};
template< class T >
T* CThreadSafeClassMemoryPool<T>::Alloc()
{
T *pRet = (T*)CThreadSafeMemoryPool::Alloc();
if ( pRet )
{
Construct( pRet );
}
return pRet;
}
template< class T >
void CThreadSafeClassMemoryPool<T>::Free(T *pMem)
{
if ( pMem )
{
Destruct( pMem );
}
CThreadSafeMemoryPool::Free( pMem );
}
#endif // TSMEMPOOL_H

View File

@@ -0,0 +1,351 @@
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <stdafx.h>
#include "tier0/t0constants.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const int k_cubMemBlockPrefixSize = sizeof(uint32);
#define ALLOCSIZE_TO_LOOKUP( cubAlloc ) ( (cubAlloc - 1) >> 5 )
#define LOOKUP_TO_ALLOCSIZE( iLookup ) ( (iLookup << 5) + 1 )
//-----------------------------------------------------------------------------
// Purpose: constructor, the sizes in pMemPoolConfig must be in ascending order
//-----------------------------------------------------------------------------
CThreadSafeMultiMemoryPool::CThreadSafeMultiMemoryPool( const MemPoolConfig_t *pMemPoolConfig, int cnMemPoolConfig, int nGrowMode /*= GROW_FAST*/ )
{
m_cubReallocedTotal = 0;
m_MapRawAllocation.SetLessFunc( DefLessFunc( void * ) );
for ( int iMemPoolConfig = 0; iMemPoolConfig < cnMemPoolConfig; iMemPoolConfig++ )
{
MemPoolRecord_t memPoolRecord;
// verify that the mem pool sizes are in ascending order
Assert( iMemPoolConfig == 0 || ( iMemPoolConfig > 0 && pMemPoolConfig[ iMemPoolConfig - 1 ].m_cubBlockSize < pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize ) );
AssertMsg( pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize % 32 == 0, "Mempools sizes must be multiples of 32" );
// add an int to the block size so we can note the alloc size
memPoolRecord.m_pMemPool = new CThreadSafeMemoryPool( pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize + k_cubMemBlockPrefixSize,
pMemPoolConfig[ iMemPoolConfig ].m_cubDefaultPoolSize, nGrowMode );
Assert( memPoolRecord.m_pMemPool );
memPoolRecord.m_nBlockSize = pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize;
m_VecMemPool.AddToTail( memPoolRecord );
// update the largest blocksize
m_nBlockSizeMax = MAX( m_nBlockSizeMax, memPoolRecord.m_nBlockSize );
}
// build the lookup table
int nLookupMax = m_nBlockSizeMax >> 5;
m_VecMemPoolLookup.AddMultipleToTail( nLookupMax );
for ( int i = 0; i < nLookupMax; i++ )
{
uint32 cubAllocSize = LOOKUP_TO_ALLOCSIZE( i );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
if ( m_VecMemPool[iMemPool].m_nBlockSize >= cubAllocSize )
{
m_VecMemPoolLookup[i] = &m_VecMemPool[iMemPool];
break;
}
}
}
#if defined(_DEBUG)
// validate the lookup table
for ( int i = 1; i < (int)m_nBlockSizeMax; i++ )
{
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
if ( (int)m_VecMemPool[iMemPool].m_nBlockSize >= i )
{
AssertMsg( m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP(i)] == &m_VecMemPool[iMemPool], "Invalid mempool block size, can't generate lookup table" );
break;
}
}
}
#endif // _DEBUG
}
//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CThreadSafeMultiMemoryPool::~CThreadSafeMultiMemoryPool()
{
AUTO_LOCK( m_mutexRawAllocations );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool ++ )
{
delete m_VecMemPool[iMemPool].m_pMemPool;
}
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
}
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a block of memory at of least nAllocSize bytes
// Input : nAllocSize - number of bytes to alloc
// Output : pointer to memory alloc'd, NULL on error
//-----------------------------------------------------------------------------
void *CThreadSafeMultiMemoryPool::Alloc( uint32 cubAllocSize )
{
if ( cubAllocSize == 0 )
return NULL;
if ( cubAllocSize <= m_nBlockSizeMax )
{
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
void *pvMem = pMemPoolRecord->m_pMemPool->Alloc( cubAllocSize + k_cubMemBlockPrefixSize );
*(uint32 *)pvMem = cubAllocSize;
return ( (char *)pvMem + k_cubMemBlockPrefixSize );
}
// can't fit in our mem pools, alloc it in our one off buffer
RawAllocation_t rawAllocation;
rawAllocation.m_nBlockSize = cubAllocSize;
rawAllocation.m_pvMem = PvAlloc( cubAllocSize + k_cubMemBlockPrefixSize );
if ( !rawAllocation.m_pvMem )
{
return NULL;
}
*(uint32 *)rawAllocation.m_pvMem = rawAllocation.m_nBlockSize;
AUTO_LOCK( m_mutexRawAllocations );
m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
return ( (char *)rawAllocation.m_pvMem + k_cubMemBlockPrefixSize );
}
//-----------------------------------------------------------------------------
// Purpose: Free a previously alloc'd block
// Input : pMem - memory to free
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::Free( void *pvMem )
{
if ( !pvMem )
return;
uint32 cubAllocSize = *( (uint32 *)pvMem - 1 );
if ( cubAllocSize <= m_nBlockSizeMax )
{
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
pMemPoolRecord->m_pMemPool->Free( (char *)pvMem - k_cubMemBlockPrefixSize, cubAllocSize + k_cubMemBlockPrefixSize );
return;
}
AUTO_LOCK( m_mutexRawAllocations );
// must have been alloc'd from the raw heap, find it in map
void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
{
AssertMsg3( false, "CThreadSafeMultiMemoryPool::Free: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
pvMem, pvAllocedMem, cubAllocSize );
return;
}
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
m_MapRawAllocation.RemoveAt( iRawAllocation);
}
//-----------------------------------------------------------------------------
// Purpose: Return the size alloc'd for this block
// Input : pMem - memory to report
// Output : size in bytes of this memory
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CubAllocSize(void *pvMem)
{
if ( !pvMem )
{
return -1;
}
return *(((uint32 *)pvMem) -1);
}
//-----------------------------------------------------------------------------
// Purpose: Frees all previously alloc'd memory
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::Clear()
{
AUTO_LOCK( m_mutexRawAllocations );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
m_VecMemPool[iMemPool].m_pMemPool->Clear();
}
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
}
m_MapRawAllocation.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: print to the console info about our storage
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::PrintStats()
{
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
m_VecMemPool[iMemPool].m_pMemPool->PrintStats();
}
int cubRawBytesAllocd = 0;
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
Msg( "Raw bytes alloc'd: %s\n", Q_pretifymem( cubRawBytesAllocd, 2, true ) );
Msg( "Cumulative bytes re-alloced: %s\n", Q_pretifymem( m_cubReallocedTotal, 2, true ) );
}
//-----------------------------------------------------------------------------
// Purpose: return the total mem alloced by this pool in MB
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CMBPoolSize() const
{
uint64 cubRawBytesAllocd = 0;
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubTotalSize() );
}
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
return ( cubRawBytesAllocd / k_nMegabyte );
}
//-----------------------------------------------------------------------------
// Purpose: return the total mem alloced by this pool in MB
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CMBPoolSizeInUse() const
{
uint64 cubRawBytesAllocd = 0;
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubSizeInUse() );
}
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
return ( cubRawBytesAllocd / k_nMegabyte );
}
//-----------------------------------------------------------------------------
// Purpose: return number of mempool blocks alloc'd
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::Count()
{
int cCount = 0;
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cCount += m_VecMemPool[iMemPool].m_pMemPool->Count();
}
return cCount;
}
//-----------------------------------------------------------------------------
// Purpose: reallocate an existing block of memory to a new size (and copy the data
// Input: pvMem - a pointer to the existing memory
// cubAlloc - number of bytes to alloc
// Output: returns a pointer to the memory allocated (NULL on error)
//-----------------------------------------------------------------------------
void *CThreadSafeMultiMemoryPool::ReAlloc( void *pvMem, uint32 cubAlloc )
{
uint32 cubOldAlloc = CubAllocSize(pvMem);
if ( pvMem && cubAlloc <= cubOldAlloc )
return pvMem;
if ( cubOldAlloc > m_nBlockSizeMax )
{
AUTO_LOCK( m_mutexRawAllocations );
// okay, must have been alloc'd from the raw heap, search for it
void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
{
AssertMsg3( false, "CThreadSafeMultiMemoryPool::ReAlloc: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
pvMem, pvAllocedMem, cubOldAlloc );
return NULL;
}
// realloc the memory
void *pvNewMem = PvRealloc( pvAllocedMem, cubAlloc + k_cubMemBlockPrefixSize );
if ( !pvNewMem )
{
m_MapRawAllocation.RemoveAt( iRawAllocation );
return NULL;
}
// update our tracking
*(uint32 *)pvNewMem = cubAlloc;
if ( pvAllocedMem == pvNewMem )
{
// if pointer is the same, use the same map entry with the same key (the pointer given to caller)
m_MapRawAllocation[iRawAllocation].m_pvMem = pvNewMem;
m_MapRawAllocation[iRawAllocation].m_nBlockSize = cubAlloc;
}
else
{
// if pointer changed, need to remove the old entry and re-insert with new key
m_MapRawAllocation.RemoveAt( iRawAllocation );
RawAllocation_t rawAllocation;
rawAllocation.m_pvMem = pvNewMem;
rawAllocation.m_nBlockSize = cubAlloc;
m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
}
return ( (char *)pvNewMem + k_cubMemBlockPrefixSize );
}
else
{
// see if we can stay in the same block
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubOldAlloc )];
if ( cubAlloc <= pMemPoolRecord->m_nBlockSize )
{
// re-assign the size
*((uint32 *)pvMem - 1) = cubAlloc;
return pvMem;
}
void *pvNewMem = Alloc( cubAlloc );
if ( !pvNewMem )
{
return NULL;
}
m_cubReallocedTotal += cubOldAlloc;
Q_memcpy( pvNewMem, pvMem, cubOldAlloc );
Free( pvMem ); // now free the old memory buffer we had
return pvNewMem;
}
}

View File

@@ -0,0 +1,93 @@
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#ifndef TSMULTIMEMPOOL_H
#define TSMULTIMEMPOOL_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlmap.h"
#include "tier1/mempool.h"
#include "tier1/tsmempool.h"
//-----------------------------------------------------------------------------
// Purpose: A container of a range of mem pool sizes (for network buffers for example)
// and a raw alloc capability (for sizes greater than any contained mem pool).
//-----------------------------------------------------------------------------
class CThreadSafeMultiMemoryPool
{
public:
struct MemPoolConfig_t
{
uint32 m_cubBlockSize;
uint32 m_cubDefaultPoolSize;
};
CThreadSafeMultiMemoryPool( const MemPoolConfig_t *pnBlock, int cnMemPoolConfig, int nGrowMode = CUtlMemoryPool::GROW_FAST );
~CThreadSafeMultiMemoryPool();
// Allocate a block of at least nAllocSize bytes
void* Alloc( uint32 cubAlloc );
// Free a previously alloc'd block
void Free(void *pvMem);
// ReAllocate a previously allocated block to a new size
void* ReAlloc( void *pvMem, uint32 cubAlloc );
// Frees everything
void Clear();
// alloc size for this bit of alloc'd memory
int CubAllocSize( void *pvMem );
// prints details about our contained memory
void PrintStats();
// total number of alloc'd elements
int Count();
// Return the total size in MB allocated for this pool
int CMBPoolSize() const;
// Return the amount of memory in use
int CMBPoolSizeInUse() const;
private:
struct MemPoolRecord_t
{
CThreadSafeMemoryPool *m_pMemPool;
uint32 m_nBlockSize;
};
CUtlVector<MemPoolRecord_t> m_VecMemPool; // stores our list of mem pools
uint32 m_nBlockSizeMax;
CUtlVector<MemPoolRecord_t *> m_VecMemPoolLookup; // quick lookup table of mempools
struct RawAllocation_t
{
void *m_pvMem;
uint32 m_nBlockSize;
};
CUtlMap<void *,RawAllocation_t,int> m_MapRawAllocation; // stores our list of raw alloc'd mem
CThreadFastMutex m_mutexRawAllocations;
uint32 m_cubReallocedTotal;
};
#endif // TSMULTIMEMPOOL_H

View File

@@ -0,0 +1,693 @@
//========= Copyright Valve Corporation, All rights reserved. =================//
//
// Purpose: index-based hash map container well suited for large and growing
// datasets. It uses less memory than other hash maps and incrementally
// rehashes to reduce reallocation spikes.
//
//=============================================================================//
#ifndef UTLHASHMAPLARGE_H
#define UTLHASHMAPLARGE_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/dbg.h"
#include "bitvec.h"
#include "murmurhash3.h"
// fast mod for power of 2 numbers
namespace basetypes
{
template <class T>
inline bool IsPowerOf2(T n)
{
return n > 0 && (n & (n-1)) == 0;
}
template <class T1, class T2>
inline T2 ModPowerOf2(T1 a, T2 b)
{
return T2(a) & (b-1);
}
}
// default comparison operator
template <typename T>
class CDefEquals
{
public:
CDefEquals() {}
CDefEquals( int i ) {}
inline bool operator()( const T &lhs, const T &rhs ) const { return ( lhs == rhs ); }
inline bool operator!() const { return false; }
};
// Specialization to compare pointers
template <typename T>
class CDefEquals<T*>
{
public:
CDefEquals() {}
CDefEquals( int i ) {}
inline bool operator()( const T *lhs, const T *rhs ) const
{
if ( lhs == rhs )
return true;
else if ( NULL == lhs || NULL == rhs )
return false;
else
return ( *lhs == *rhs );
}
inline bool operator!() const { return false; }
};
// Hash specialization for CUtlStrings
template<>
struct MurmurHash3Functor<CUtlString>
{
typedef uint32 TargetType ;
TargetType operator()(const CUtlString &strKey) const
{
return MurmurHash3Functor<const char*>()( strKey.String() );
}
};
//hash 3 function for a general case sensitive string compares
struct MurmurHash3ConstCharPtr
{
typedef uint32 TargetType ;
TargetType operator()( const char* pszKey ) const { return MurmurHash3Functor<const char*>()( pszKey ); }
};
struct CaseSensitiveStrEquals
{
bool operator()( const char* pszLhs, const char* pszRhs ) const { return strcmp( pszLhs, pszRhs ) == 0; }
};
//-----------------------------------------------------------------------------
//
// Purpose: An associative container. Pretty much identical to CUtlMap without the ability to walk in-order
// This container is well suited for large and growing datasets. It uses less
// memory than other hash maps and incrementally rehashes to reduce reallocation spikes.
// However, it is slower (by about 20%) than CUtlHashTable
//
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L = CDefEquals<K>, typename H = MurmurHash3Functor<K> >
class CUtlHashMapLarge : public base_utlmap_t
{
public:
// This enum exists so that FOR_EACH_MAP and FOR_EACH_MAP_FAST cannot accidentally
// be used on a type that is not a CUtlMap. If the code compiles then all is well.
// The check for IsUtlMap being true should be free.
// Using an enum rather than a static const bool ensures that this trick works even
// with optimizations disabled on gcc.
enum CompileTimeCheck
{
IsUtlMap = 1
};
typedef K KeyType_t;
typedef T ElemType_t;
typedef int IndexType_t;
typedef L EqualityFunc_t;
typedef H HashFunc_t;
CUtlHashMapLarge()
{
m_cElements = 0;
m_nMaxElement = 0;
m_nMinRehashedBucket = InvalidIndex();
m_nMaxRehashedBucket = InvalidIndex();
m_iNodeFreeListHead = InvalidIndex();
}
CUtlHashMapLarge( int cElementsExpected )
{
m_cElements = 0;
m_nMaxElement = 0;
m_nMinRehashedBucket = InvalidIndex();
m_nMaxRehashedBucket = InvalidIndex();
m_iNodeFreeListHead = InvalidIndex();
EnsureCapacity( cElementsExpected );
}
~CUtlHashMapLarge()
{
RemoveAll();
}
// gets particular elements
ElemType_t & Element( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; }
const ElemType_t & Element( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; }
ElemType_t & operator[]( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; }
const ElemType_t & operator[]( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; }
KeyType_t & Key( IndexType_t i ) { return m_memNodes.Element( i ).m_key; }
const KeyType_t & Key( IndexType_t i ) const { return m_memNodes.Element( i ).m_key; }
// Num elements
IndexType_t Count() const { return m_cElements; }
// Max "size" of the vector
IndexType_t MaxElement() const { return m_nMaxElement; }
// Checks if a node is valid and in the map
bool IsValidIndex( IndexType_t i ) const { return i >= 0 && i < m_nMaxElement && !IsFreeNodeID( m_memNodes[i].m_iNextNode ); }
// Invalid index
static IndexType_t InvalidIndex() { return -1; }
// Insert method
IndexType_t Insert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); }
IndexType_t Insert( const KeyType_t &key ) { return InsertInternal( key, ElemType_t(), eInsert_UpdateExisting ); }
IndexType_t InsertWithDupes( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_CreateDupes ); }
IndexType_t FindOrInsert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_LeaveExisting ); }
IndexType_t InsertOrReplace( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); }
// Finds an element
IndexType_t Find( const KeyType_t &key ) const;
// has an element
bool HasElement( const KeyType_t &key ) const
{
return Find( key ) != InvalidIndex();
}
void EnsureCapacity( int num );
void RemoveAt( IndexType_t i );
bool Remove( const KeyType_t &key )
{
int iMap = Find( key );
if ( iMap != InvalidIndex() )
{
RemoveAt( iMap );
return true;
}
return false;
}
void RemoveAll();
void Purge();
void PurgeAndDeleteElements();
void Swap( CUtlHashMapLarge<K,T,L,H> &rhs )
{
m_vecHashBuckets.Swap( rhs.m_vecHashBuckets );
V_swap( m_bitsMigratedBuckets, rhs.m_bitsMigratedBuckets );
m_memNodes.Swap( rhs.m_memNodes );
V_swap( m_iNodeFreeListHead, rhs.m_iNodeFreeListHead );
V_swap( m_cElements, rhs.m_cElements );
V_swap( m_nMaxElement, rhs.m_nMaxElement );
V_swap( m_nMinRehashedBucket, rhs.m_nMinRehashedBucket );
V_swap( m_nMaxRehashedBucket, rhs.m_nMaxRehashedBucket );
V_swap( m_EqualityFunc, rhs.m_EqualityFunc );
V_swap( m_HashFunc, rhs.m_HashFunc );
}
private:
enum EInsertPolicy { eInsert_UpdateExisting, eInsert_LeaveExisting, eInsert_CreateDupes };
IndexType_t InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy );
inline IndexType_t FreeNodeIDToIndex( IndexType_t i ) const { return (0-i)-3; }
inline IndexType_t FreeNodeIndexToID( IndexType_t i ) const { return (-3)-i; }
inline bool IsFreeNodeID( IndexType_t i ) const { return i < InvalidIndex(); }
int FindInBucket( int iBucket, const KeyType_t &key ) const;
int AllocNode();
void RehashNodesInBucket( int iBucket );
void LinkNodeIntoBucket( int iBucket, int iNewNode );
void UnlinkNodeFromBucket( int iBucket, int iNewNode );
bool RemoveNodeFromBucket( int iBucket, int iNodeToRemove );
void IncrementalRehash();
struct HashBucket_t
{
IndexType_t m_iNode;
};
CUtlVector<HashBucket_t> m_vecHashBuckets;
CLargeVarBitVec m_bitsMigratedBuckets;
struct Node_t
{
KeyType_t m_key;
ElemType_t m_elem;
int m_iNextNode;
};
CUtlMemory<Node_t> m_memNodes;
IndexType_t m_iNodeFreeListHead;
IndexType_t m_cElements;
IndexType_t m_nMaxElement;
IndexType_t m_nMinRehashedBucket, m_nMaxRehashedBucket;
EqualityFunc_t m_EqualityFunc;
HashFunc_t m_HashFunc;
};
//-----------------------------------------------------------------------------
// Purpose: inserts an item into the map
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy )
{
// make sure we have room in the hash table
if ( m_cElements >= m_vecHashBuckets.Count() )
EnsureCapacity( MAX( 16, m_vecHashBuckets.Count() * 2 ) );
if ( m_cElements >= m_memNodes.Count() )
m_memNodes.Grow( m_memNodes.Count() * 2 );
// rehash incrementally
IncrementalRehash();
// hash the item
uint32 hash = m_HashFunc( key );
// migrate data forward, if necessary
int cBucketsToModAgainst = m_vecHashBuckets.Count() >> 1;
int iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst);
while ( iBucket >= m_nMinRehashedBucket
&& !m_bitsMigratedBuckets.Get( iBucket ) )
{
RehashNodesInBucket( iBucket );
cBucketsToModAgainst >>= 1;
iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst);
}
// prevent duplicates if necessary
if ( ( ePolicy != eInsert_CreateDupes ) && m_cElements )
{
// look in the bucket to see if we have a conflict
int iBucket2 = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
IndexType_t iNode = FindInBucket( iBucket2, key );
if ( iNode != InvalidIndex() )
{
// a duplicate - update in place (matching CUtlMap)
if( ePolicy == eInsert_UpdateExisting )
{
m_memNodes[iNode].m_elem = insert;
}
return iNode;
}
}
// make an item
int iNewNode = AllocNode();
m_memNodes[iNewNode].m_iNextNode = InvalidIndex();
CopyConstruct( &m_memNodes[iNewNode].m_key, key );
CopyConstruct( &m_memNodes[iNewNode].m_elem, insert );
iBucket = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
// link ourselves in
// ::OutputDebugStr( CFmtStr( "insert %d into bucket %d\n", key, iBucket ).Access() );
LinkNodeIntoBucket( iBucket, iNewNode );
// return the new node
return iNewNode;
}
//-----------------------------------------------------------------------------
// Purpose: grows the map to fit the specified amount
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::EnsureCapacity( int amount )
{
m_memNodes.EnsureCapacity( amount );
// ::OutputDebugStr( CFmtStr( "grown m_memNodes from %d to %d\n", m_cElements, m_memNodes.Count() ).Access() );
if ( amount <= m_vecHashBuckets.Count() )
return;
int cBucketsNeeded = MAX( 16, m_vecHashBuckets.Count() );
while ( cBucketsNeeded < amount )
cBucketsNeeded *= 2;
// ::OutputDebugStr( CFmtStr( "grown m_vecHashBuckets from %d to %d\n", m_vecHashBuckets.Count(), cBucketsNeeded ).Access() );
// grow the hash buckets
int grow = cBucketsNeeded - m_vecHashBuckets.Count();
int iFirst = m_vecHashBuckets.AddMultipleToTail( grow );
// clear all the new data to invalid bits
memset( &m_vecHashBuckets[iFirst], 0xFFFFFFFF, grow*sizeof(m_vecHashBuckets[iFirst]) );
Assert( basetypes::IsPowerOf2( m_vecHashBuckets.Count() ) );
// we'll have to rehash, all the buckets that existed before growth
m_nMinRehashedBucket = 0;
m_nMaxRehashedBucket = iFirst;
if ( m_cElements > 0 )
{
// remove all the current bits
m_bitsMigratedBuckets.Resize( 0 );
// re-add new bits; these will all be reset to 0
m_bitsMigratedBuckets.Resize( m_vecHashBuckets.Count() );
}
else
{
// no elements - no rehashing
m_nMinRehashedBucket = m_vecHashBuckets.Count();
}
}
//-----------------------------------------------------------------------------
// Purpose: gets a new node, from the free list if possible
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::AllocNode()
{
// if we're out of free elements, get the max
if ( m_cElements == m_nMaxElement )
{
m_cElements++;
return m_nMaxElement++;
}
// pull from the free list
Assert( m_iNodeFreeListHead != InvalidIndex() );
int iNewNode = m_iNodeFreeListHead;
m_iNodeFreeListHead = FreeNodeIDToIndex( m_memNodes[iNewNode].m_iNextNode );
m_cElements++;
return iNewNode;
}
//-----------------------------------------------------------------------------
// Purpose: takes a bucket of nodes and re-hashes them into a more optimal bucket
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::RehashNodesInBucket( int iBucketSrc )
{
// mark us as migrated
m_bitsMigratedBuckets.Set( iBucketSrc );
// walk the list of items, re-hashing them
IndexType_t iNode = m_vecHashBuckets[iBucketSrc].m_iNode;
while ( iNode != InvalidIndex() )
{
IndexType_t iNodeNext = m_memNodes[iNode].m_iNextNode;
Assert( iNodeNext != iNode );
// work out where the node should go
const KeyType_t &key = m_memNodes[iNode].m_key;
uint32 hash = m_HashFunc( key );
int iBucketDest = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
// if the hash bucket has changed, move it
if ( iBucketDest != iBucketSrc )
{
// ::OutputDebugStr( CFmtStr( "moved key %d from bucket %d to %d\n", key, iBucketSrc, iBucketDest ).Access() );
// remove from this bucket list
UnlinkNodeFromBucket( iBucketSrc, iNode );
// link into new bucket list
LinkNodeIntoBucket( iBucketDest, iNode );
}
iNode = iNodeNext;
}
}
//-----------------------------------------------------------------------------
// Purpose: searches for an item by key, returning the index handle
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::Find( const KeyType_t &key ) const
{
if ( m_cElements == 0 )
return InvalidIndex();
// hash the item
uint32 hash = m_HashFunc( key );
// find the bucket
int cBucketsToModAgainst = m_vecHashBuckets.Count();
int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
// look in the bucket for the item
int iNode = FindInBucket( iBucket, key );
if ( iNode != InvalidIndex() )
return iNode;
// not found? we may have to look in older buckets
cBucketsToModAgainst >>= 1;
while ( cBucketsToModAgainst >= m_nMinRehashedBucket )
{
iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
if ( !m_bitsMigratedBuckets.Get( iBucket ) )
{
int iNode2 = FindInBucket( iBucket, key );
if ( iNode2 != InvalidIndex() )
return iNode2;
}
cBucketsToModAgainst >>= 1;
}
return InvalidIndex();
}
//-----------------------------------------------------------------------------
// Purpose: searches for an item by key, returning the index handle
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::FindInBucket( int iBucket, const KeyType_t &key ) const
{
if ( m_vecHashBuckets[iBucket].m_iNode != InvalidIndex() )
{
IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode;
Assert( iNode < m_nMaxElement );
while ( iNode != InvalidIndex() )
{
// equality check
if ( m_EqualityFunc( key, m_memNodes[iNode].m_key ) )
return iNode;
iNode = m_memNodes[iNode].m_iNextNode;
}
}
return InvalidIndex();
}
//-----------------------------------------------------------------------------
// Purpose: links a node into a bucket
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
void CUtlHashMapLarge<K,T,L,H>::LinkNodeIntoBucket( int iBucket, int iNewNode )
{
// add into the start of the bucket's list
m_memNodes[iNewNode].m_iNextNode = m_vecHashBuckets[iBucket].m_iNode;
m_vecHashBuckets[iBucket].m_iNode = iNewNode;
}
//-----------------------------------------------------------------------------
// Purpose: unlinks a node from the bucket
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
void CUtlHashMapLarge<K,T,L,H>::UnlinkNodeFromBucket( int iBucket, int iNodeToUnlink )
{
int iNodeNext = m_memNodes[iNodeToUnlink].m_iNextNode;
// if it's the first node, just update the bucket to point to the new place
int iNode = m_vecHashBuckets[iBucket].m_iNode;
if ( iNode == iNodeToUnlink )
{
m_vecHashBuckets[iBucket].m_iNode = iNodeNext;
return;
}
// walk the list to find where
while ( iNode != InvalidIndex() )
{
if ( m_memNodes[iNode].m_iNextNode == iNodeToUnlink )
{
m_memNodes[iNode].m_iNextNode = iNodeNext;
return;
}
iNode = m_memNodes[iNode].m_iNextNode;
}
// should always be valid to unlink
Assert( false );
}
//-----------------------------------------------------------------------------
// Purpose: removes a single item from the map
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::RemoveAt( IndexType_t i )
{
if ( !IsValidIndex( i ) )
{
Assert( false );
return;
}
// unfortunately, we have to re-hash to find which bucket we're in
uint32 hash = m_HashFunc( m_memNodes[i].m_key );
int cBucketsToModAgainst = m_vecHashBuckets.Count();
int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
if ( RemoveNodeFromBucket( iBucket, i ) )
return;
// wasn't found; look in older buckets
cBucketsToModAgainst >>= 1;
while ( cBucketsToModAgainst >= m_nMinRehashedBucket )
{
iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
if ( !m_bitsMigratedBuckets.Get( iBucket ) )
{
if ( RemoveNodeFromBucket( iBucket, i ) )
return;
}
cBucketsToModAgainst >>= 1;
}
// never found, container is busted
Assert( false );
}
//-----------------------------------------------------------------------------
// Purpose: removes a node from the bucket, return true if it was found
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline bool CUtlHashMapLarge<K,T,L,H>::RemoveNodeFromBucket( IndexType_t iBucket, int iNodeToRemove )
{
IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode;
while ( iNode != InvalidIndex() )
{
if ( iNodeToRemove == iNode )
{
// found it, remove
UnlinkNodeFromBucket( iBucket, iNodeToRemove );
Destruct( &m_memNodes[iNode].m_key );
Destruct( &m_memNodes[iNode].m_elem );
// link into free list
m_memNodes[iNode].m_iNextNode = FreeNodeIndexToID( m_iNodeFreeListHead );
m_iNodeFreeListHead = iNode;
m_cElements--;
if ( m_cElements == 0 )
{
m_nMinRehashedBucket = m_vecHashBuckets.Count();
}
return true;
}
iNode = m_memNodes[iNode].m_iNextNode;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: removes all items from the hash map
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::RemoveAll()
{
FOR_EACH_MAP_FAST( *this, i )
{
Destruct( &m_memNodes[i].m_key );
Destruct( &m_memNodes[i].m_elem );
}
m_cElements = 0;
m_nMaxElement = 0;
m_iNodeFreeListHead = InvalidIndex();
m_nMinRehashedBucket = m_vecHashBuckets.Count();
m_nMaxRehashedBucket = InvalidIndex();
m_bitsMigratedBuckets.Resize( 0 );
memset( m_vecHashBuckets.Base(), 0xFF, m_vecHashBuckets.Count() * sizeof(HashBucket_t) );
}
//-----------------------------------------------------------------------------
// Purpose: removes all items from the hash map and releases memory
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::Purge()
{
FOR_EACH_MAP_FAST( *this, i )
{
Destruct( &m_memNodes[i].m_key );
Destruct( &m_memNodes[i].m_elem );
}
m_cElements = 0;
m_nMaxElement = 0;
m_iNodeFreeListHead = InvalidIndex();
m_nMinRehashedBucket = InvalidIndex();
m_nMaxRehashedBucket = InvalidIndex();
m_bitsMigratedBuckets.Resize( 0 );
m_memNodes.Purge();
m_vecHashBuckets.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: removes and deletes all items from the hash map and releases memory
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::PurgeAndDeleteElements()
{
FOR_EACH_MAP_FAST( *this, i )
{
delete this->Element( i );
}
Purge();
}
//-----------------------------------------------------------------------------
// Purpose: rehashes buckets
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::IncrementalRehash()
{
if ( m_nMinRehashedBucket < m_nMaxRehashedBucket )
{
while ( m_nMinRehashedBucket < m_nMaxRehashedBucket )
{
// see if the bucket needs rehashing
if ( m_vecHashBuckets[m_nMinRehashedBucket].m_iNode != InvalidIndex()
&& !m_bitsMigratedBuckets.Get(m_nMinRehashedBucket) )
{
// rehash this bucket
RehashNodesInBucket( m_nMinRehashedBucket );
// only actively do one - don't want to do it too fast since we may be on a rapid growth path
++m_nMinRehashedBucket;
break;
}
// nothing to rehash in that bucket - increment and look again
++m_nMinRehashedBucket;
}
if ( m_nMinRehashedBucket >= m_nMaxRehashedBucket )
{
// we're done; don't need any bits anymore
m_nMinRehashedBucket = m_vecHashBuckets.Count();
m_nMaxRehashedBucket = InvalidIndex();
m_bitsMigratedBuckets.Resize( 0 );
}
}
}
#endif // UTLHASHMAPLARGE_H

View File

@@ -0,0 +1,14 @@
//========= Copyright Valve Corporation, All rights reserved. =================//
//
// Purpose: Larger string builder functions go here.
//
//=============================================================================//
#include <stdafx.h>
#include "tier0/t0constants.h"
#include "utlstringbuilder.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

View File

@@ -0,0 +1,44 @@
//====== Copyright © Valve Corporation, All rights reserved. ==================
//
// Purpose: String class thats more suited to frequent modification/appends
// than CUtlString. Copied from Steam's tier1 utlstring.h instead of
// a full utlstring.h merge because the files differed nearly 100%.
//
//=============================================================================
#ifndef UTLSTRINGBUILDER_H
#define UTLSTRINGBUILDER_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlmemory.h"
#include "tier1/strtools.h"
#include "tier1/utlstring.h"
#if 0
#include "limits.h"
#include "tier1/utlbinaryblock.h"
#endif
//-----------------------------------------------------------------------------
// Data and memory validation
//-----------------------------------------------------------------------------
#ifdef DBGFLAG_VALIDATE
inline void CUtlStringBuilder::Validate( CValidator &validator, const char *pchName )
{
#ifdef _WIN32
validator.Push( typeid(*this).raw_name(), this, pchName );
#else
validator.Push( typeid(*this).name(), this, pchName );
#endif
if ( m_data.IsHeap() )
validator.ClaimMemory( Access() );
validator.Pop();
}
#endif // DBGFLAG_VALIDATE
#endif // UTLSTRINGBUILDER_H

799
gcsdk/steammessages.proto Normal file
View File

@@ -0,0 +1,799 @@
//====== Copyright 1996-2010, Valve Corporation, All rights reserved. =======
//
// Purpose: The file defines our Google Protocol Buffers which are used in over
// the wire messages between servers as well as between clients and servers.
//
//=============================================================================
// We care more about speed than code size
option optimize_for = SPEED;
// We don't use the service generation functionality
option cc_generic_services = false;
//
// STYLE NOTES:
//
// Use CamelCase CMsgMyMessageName style names for messages.
//
// Use lowercase _ delimited names like my_steam_id for field names, this is non-standard for Steam,
// but plays nice with the Google formatted code generation.
//
// Try not to use required fields ever. Only do so if you are really really sure you'll never want them removed.
// Optional should be preffered as it will make versioning easier and cleaner in the future if someone refactors
// your message and wants to remove or rename fields.
//
// Use fixed64 for JobId_t, GID_t, or SteamID. This is appropriate for any field that is normally
// going to be larger than 2^56. Otherwise use int64 for 64 bit values that are frequently smaller
// than 2^56 as it will safe space on the wire in those cases.
//
// Similar to fixed64, use fixed32 for RTime32 or other 32 bit values that are frequently larger than
// 2^28. It will safe space in those cases, otherwise use int32 which will safe space for smaller values.
// An exception to this rule for RTime32 is if the value will frequently be zero rather than set to an actual
// time.
//
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions {
optional bool key_field = 60000 [ default = false ];
}
extend google.protobuf.MessageOptions{
// Allows us to customize the pooling for different messages
optional int32 msgpool_soft_limit = 60000 [default=32];
optional int32 msgpool_hard_limit = 60001 [default=384];
}
enum GCProtoBufMsgSrc
{
GCProtoBufMsgSrc_Unspecified = 0;
GCProtoBufMsgSrc_FromSystem = 1;
GCProtoBufMsgSrc_FromSteamID = 2;
GCProtoBufMsgSrc_FromGC = 3;
GCProtoBufMsgSrc_ReplySystem = 4;
};
//
// Message header, every protcol buffer based message starts with this.
//
message CMsgProtoBufHeader
{
option (msgpool_soft_limit) = 256;
option (msgpool_hard_limit) = 1024;
// All fields here are optional.
// Client message header fields
optional fixed64 client_steam_id = 1; // SteamID of the client sending this, typically set in all client originated messages.
optional int32 client_session_id = 2; // SessionID of the client on the CM
// Source appId for inter-gc messages
optional uint32 source_app_id = 3; // appId of source GC message sender
// Job routing (may be set on client or inter-server messages)
optional fixed64 job_id_source = 10 [ default = 0xFFFFFFFFFFFFFFFF ]; // JobID that sent this message
optional fixed64 job_id_target = 11 [ default = 0xFFFFFFFFFFFFFFFF ]; // The target job which is expected to be waiting on this message
optional string target_job_name = 12; // the type of job to start when this message is received
optional int32 eresult = 13 [default = 2]; // For response jobs, the corresponding eresult
optional string error_message = 14; // Optionally an error message in case of failure. Mostly used for debugging purpose.
// Where did this message originally enter the system? From a client, from another GC, etc
optional GCProtoBufMsgSrc gc_msg_src = 200;
// If this came from another GC, what is the GC that it came from
optional uint32 gc_dir_index_source = 201;
}
//
// Used to serialize CWebAPIKey objects.
//
message CMsgWebAPIKey
{
optional uint32 status = 1 [ default = 0xFF ];
optional uint32 account_id = 2 [ default = 0 ];
optional uint32 publisher_group_id = 3 [ default = 0 ];
optional uint32 key_id = 4;
optional string domain = 5;
}
//
// An HTTP request message
//
message CMsgHttpRequest
{
message RequestHeader
{
optional string name = 1;
optional string value = 2;
}
message QueryParam
{
optional string name = 1;
optional bytes value = 2;
}
optional uint32 request_method = 1;
optional string hostname = 2;
optional string url = 3;
repeated RequestHeader headers = 4;
repeated QueryParam get_params = 5;
repeated QueryParam post_params = 6;
optional bytes body = 7;
optional uint32 absolute_timeout = 8;
}
//
// A web API request
//
message CMsgWebAPIRequest
{
optional string UNUSED_job_name = 1; // no longer used
optional string interface_name = 2;
optional string method_name = 3;
optional uint32 version = 4;
optional CMsgWebAPIKey api_key = 5;
optional CMsgHttpRequest request = 6;
optional uint32 routing_app_id = 7;
}
//
// An HTTP response
//
message CMsgHttpResponse
{
message ResponseHeader
{
optional string name = 1;
optional string value = 2;
}
optional uint32 status_code = 1;
repeated ResponseHeader headers = 2;
optional bytes body = 3;
}
//
// Message struct for k_EMsgAMFindAccounts
//
message CMsgAMFindAccounts
{
optional uint32 search_type = 1;
optional string search_string = 2;
}
//
// Message struct for k_EMsgAMFindAccountsResponse
//
message CMsgAMFindAccountsResponse
{
repeated fixed64 steam_id = 1;
}
//
// k_EMsgNotifyWatchdog
//
message CMsgNotifyWatchdog
{
optional uint32 source = 1; // Alert source
optional uint32 alert_type = 2; // type of alert
optional uint32 alert_destination = 3; // destination for alert
optional bool critical = 4; // Is the alert critical
optional uint32 time = 5; // world time that alert occurred
optional uint32 appid = 6; // app to forward the alert to for alerts with alert_type set to AppID
optional string text = 7; // Alert text
}
//
// k_EGCMsgGetLicenses
//
message CMsgAMGetLicenses
{
optional fixed64 steamid = 1; // the steam ID to fetch licenses for
}
//
// Used by CMsgAMGetLicensesResponse
//
message CMsgPackageLicense
{
optional uint32 package_id = 1; // ID of the package this license is for
optional uint32 time_created = 2; // RTime32 when the license was granted
optional uint32 owner_id = 3; // the original owner if this license. if this is different from given steamid, it's a borrowed package
}
//
// k_EMsgAMGetLicensesResponse
//
message CMsgAMGetLicensesResponse
{
repeated CMsgPackageLicense license = 1; // the list of licenses the user owns
optional uint32 result = 2; // result code, k_EResultOK on success
}
//
// k_EMsgAMGetUserGameStats
//
message CMsgAMGetUserGameStats
{
optional fixed64 steam_id = 1; // ID of user
optional fixed64 game_id = 2; // Game ID of stats to get
repeated uint32 stats = 3;
}
//
// k_EMsgAMGetUserGameStatsResponse
//
message CMsgAMGetUserGameStatsResponse
{
optional fixed64 steam_id = 1; // ID of user
optional fixed64 game_id = 2; // Game ID
optional int32 eresult = 3 [default = 2]; // EResult with result of query. (Fields following are only valid if this is EResultOK.)
message Stats
{
optional uint32 stat_id = 1;
optional uint32 stat_value = 2; // There are 4 of these, really only 8 bits each. Yay for compression!
}
message Achievement_Blocks
{
optional uint32 achievement_id = 1;
optional uint32 achievement_bit_id = 2;
optional fixed32 unlock_time = 3; // There are only 32 of these, matching the achievment bitfields, we check on the receiver that
}
repeated Stats stats = 4;
repeated Achievement_Blocks achievement_blocks = 5;
}
// k_EMsgAdminGCGetCommandList
message CMsgGCGetCommandList
{
optional uint32 app_id = 1;
optional string command_prefix = 2; // prefix of the command to filter by
};
// k_EMsgAdminGCGetCommandListResponse
message CMsgGCGetCommandListResponse
{
repeated string command_name = 1; // a list of command names
};
//
// k_EGCMsgMemCachedGet
//
message CGCMsgMemCachedGet
{
repeated string keys = 1;
}
//
// k_EGCMsgMemCachedGetResponse
//
message CGCMsgMemCachedGetResponse
{
message ValueTag
{
optional bool found = 1;
optional bytes value = 2;
}
repeated ValueTag values = 1;
}
//
// k_EGCMsgMemCachedSet
//
message CGCMsgMemCachedSet
{
message KeyPair
{
optional string name = 1;
optional bytes value = 2;
}
repeated KeyPair keys = 1;
}
//
// k_EGCMsgMemCachedDelete
//
message CGCMsgMemCachedDelete
{
repeated string keys = 1;
}
//
// k_EGCMsgMemCachedStats
//
message CGCMsgMemCachedStats
{
// Nothing, yet.
}
//
// k_EGCMsgMemCachedStatsResponse
//
message CGCMsgMemCachedStatsResponse
{
optional uint64 curr_connections = 1;
optional uint64 cmd_get = 2;
optional uint64 cmd_set = 3;
optional uint64 cmd_flush = 4;
optional uint64 get_hits = 5;
optional uint64 get_misses = 6;
optional uint64 delete_hits = 7;
optional uint64 delete_misses = 8;
optional uint64 bytes_read = 9;
optional uint64 bytes_written = 10;
optional uint64 limit_maxbytes = 11;
optional uint64 curr_items = 12;
optional uint64 evictions = 13;
optional uint64 bytes = 14;
}
//
// k_EGCMsgSQLStats
//
message CGCMsgSQLStats
{
optional uint32 schema_catalog = 1;
}
//
// k_EGCMsgSQLStatsResponse
//
message CGCMsgSQLStatsResponse
{
optional uint32 threads = 1;
optional uint32 threads_connected = 2;
optional uint32 threads_active = 3;
optional uint32 operations_submitted = 4;
optional uint32 prepared_statements_executed = 5;
optional uint32 non_prepared_statements_executed = 6;
optional uint32 deadlock_retries = 7;
optional uint32 operations_timed_out_in_queue = 8;
optional uint32 errors = 9;
}
// k_EMsgAMAddFreeLicense
message CMsgAMAddFreeLicense
{
optional fixed64 steamid = 1; // SteamID of account
optional uint32 ip_public = 2; // IP of client (zero if not a client-initiated message)
optional uint32 packageid = 3; // ID for package to purchase. Should be k_uPackageIdInvalid if shopping cart gid set
optional string store_country_code = 4; // country code to use for purchase
};
// k_EMsgAMAddFreeLicenseResponse
message CMsgAMAddFreeLicenseResponse
{
optional int32 eresult = 1 [default = 2]; // EResult with result of Purchase.
optional int32 purchase_result_detail = 2; // Detailed result information
optional fixed64 transid = 3; // ID of the created transaction
};
//
// k_EGCMsgGetIPLocation
//
message CGCMsgGetIPLocation
{
repeated fixed32 ips = 1;
}
//
// k_EGCMsgGetIPLocationResponse
//
message CIPLocationInfo
{
optional uint32 ip = 1;
optional float latitude = 2;
optional float longitude = 3;
optional string country = 4;
optional string state = 5;
optional string city = 6;
}
message CGCMsgGetIPLocationResponse
{
repeated CIPLocationInfo infos = 1;
}
//
// k_EGCMsgSystemStatsSchema
//
message CGCMsgSystemStatsSchema
{
optional uint32 gc_app_id = 1;
optional bytes schema_kv = 2;
}
//
// k_EGCMsgGetSystemStats
//
message CGCMsgGetSystemStats
{
}
//
// k_EGCMsgGetSystemStatsResponse
//
message CGCMsgGetSystemStatsResponse
{
optional uint32 gc_app_id = 1;
optional bytes stats_kv = 2;
// statically included in GCHost's stats
optional uint32 active_jobs = 3;
optional uint32 yielding_jobs = 4;
optional uint32 user_sessions = 5;
optional uint32 game_server_sessions = 6;
optional uint32 socaches = 7;
optional uint32 socaches_to_unload = 8;
optional uint32 socaches_loading = 9;
optional uint32 writeback_queue = 10;
optional uint32 steamid_locks = 11;
optional uint32 logon_queue = 12;
optional uint32 logon_jobs = 13;
}
// k_EGCMsgSendEmail
message CMsgAMSendEmail
{
message ReplacementToken
{
optional string token_name = 1;
optional string token_value = 2;
}
message PersonaNameReplacementToken
{
optional fixed64 steamid = 1;
optional string token_name = 2;
}
optional fixed64 steamid = 1;
optional uint32 email_msg_type = 2;
optional uint32 email_format = 3;
repeated PersonaNameReplacementToken persona_name_tokens = 5;
optional uint32 source_gc = 6;
repeated ReplacementToken tokens = 7;
}
// k_EGCMsgSendEmailResponse
message CMsgAMSendEmailResponse
{
optional uint32 eresult = 1 [default = 2];
}
// k_EGCMsgGetEmailTemplate
message CMsgGCGetEmailTemplate
{
optional uint32 app_id = 1;
optional uint32 email_msg_type = 2;
optional int32 email_lang = 3;
optional int32 email_format = 4;
}
// k_EGCMsgGetEmailTemplateResponse
message CMsgGCGetEmailTemplateResponse
{
optional uint32 eresult = 1 [default = 2];
optional bool template_exists = 2;
optional string template = 3;
}
// k_EMsgAMGrantGuestPasses2
message CMsgAMGrantGuestPasses2
{
optional fixed64 steam_id = 1;
optional uint32 package_id = 2;
optional int32 passes_to_grant = 3;
optional int32 days_to_expiration = 4;
optional int32 action = 5;
}
// k_EMsgAMGrantGuestPasses2Response
message CMsgAMGrantGuestPasses2Response
{
optional int32 eresult = 1 [default = 2];
optional int32 passes_granted = 2 [default = 0];
}
// k_EGCMsgGetAccountDetails
message CGCSystemMsg_GetAccountDetails
{
option (msgpool_soft_limit) = 128;
option (msgpool_hard_limit) = 512;
optional fixed64 steamid = 1; // User to get details for
optional uint32 appid = 2; // appid of the source GC
}
message CGCSystemMsg_GetAccountDetails_Response
{
option (msgpool_soft_limit) = 128;
option (msgpool_hard_limit) = 512;
optional uint32 eresult_deprecated = 1 [ default = 2 ]; // Result of the request
optional string account_name = 2; // Login name for the user
optional string persona_name = 3; // Diplay name for the user
optional bool is_profile_public = 4; // Is the user's profile public
optional bool is_inventory_public = 5; // Is the user's inventory public
//optional bool is_trusted = 6; // Is the user trusted
optional bool is_vac_banned = 7; // Is the user vac banned
optional bool is_cyber_cafe = 8; // Is the user a cybe cafe
optional bool is_school_account = 9; // Is the user a school account
optional bool is_limited = 10; // Is the user limited
optional bool is_subscribed = 11; // Is the user subscribed to this app
optional uint32 package = 12; // The package the user owns the app through
optional bool is_free_trial_account = 13; // Is the user playing the game for free
optional uint32 free_trial_expiration = 14; // If the user is playing for free, when does it expire?
optional bool is_low_violence = 15; // Is the user restricted to low-violence for this app
optional bool is_account_locked_down = 16; // Is the user's account locked
optional bool is_community_banned = 17; // Is the user banned from performing community actions
optional bool is_trade_banned = 18; // Is the user banned from trading items to other users on Steam
optional uint32 trade_ban_expiration = 19; // The time at which the user is unbanned from trading
optional uint32 accountid = 20; // The account ID of the user we're responding about
optional uint32 suspension_end_time = 21; // If suspended (ban that ends), the date/time the suspension ends
optional string currency = 22; // The currency associated with this account
optional uint32 steam_level = 23; // The Steam level of the user
optional uint32 friend_count = 24; // Number of friends
optional uint32 account_creation_time = 25; // Time when the account was created
optional bool is_steamguard_enabled = 27; // Is SteamGuard enabled
optional bool is_phone_verified = 28; // Has a verified phone number
optional bool is_two_factor_auth_enabled = 29; // Has SteamGuard two factor auth
optional uint32 two_factor_enabled_time = 30; // Time two factor was added
optional uint32 phone_verification_time = 31; // Time phone was verified
optional uint64 phone_id = 33; // Phone identifier
optional bool is_phone_identifying = 34; // Phone is identifying
}
//
// k_EGCMsgGetPersonaNames
//
message CMsgGCGetPersonaNames
{
repeated fixed64 steamids = 1; // Users whose persona names we want
}
message CMsgGCGetPersonaNames_Response
{
message PersonaName
{
optional fixed64 steamid = 1; // User we could get a name for
optional string persona_name = 2; // Display name for that user
}
repeated PersonaName succeeded_lookups = 1; // Users we could get names for
repeated fixed64 failed_lookup_steamids = 2; // Users we failed to get a names for
}
//
// k_EGCMsgCheckFriendship
//
message CMsgGCCheckFriendship
{
optional fixed64 steamid_left = 1; // User whose friends list we'll load
optional fixed64 steamid_right = 2; // User to look for in the list we load
}
message CMsgGCCheckFriendship_Response
{
optional bool success = 1; // Whether the API calls all succeeded
optional bool found_friendship = 2; // Denotes whether the users are friends (false on API failure)
}
//
// k_EGCMsgMasterSetDirectory
//
message CMsgGCMsgMasterSetDirectory
{
message SubGC
{
optional uint32 dir_index = 1; // The index in the GC directory indicating what role this GC serves
optional string name = 2; // A string to give the GC a name for asserts/logs/connection, etc
optional string box = 3; // The box that this GC is expected to be associated with
optional string command_line = 4; // Additional command line parameters to provide for this GC instance
optional string gc_binary = 5; // The binary that should be launched for this GC. This can be left blank to launch the default binary
}
optional uint32 master_dir_index = 1; // The index of the master GC so that it knows how to setup the routing tables to include the master and sub GCs
repeated SubGC dir = 2; // A listing of the various sub GCs that the GCH should create
}
message CMsgGCMsgMasterSetDirectory_Response
{
optional int32 eresult = 1 [default = 2]; // Could the GC start the processes? It doesn't mean they will all get initialized and each sub GC still needs to ack, but catches failure earlier
}
//
// k_EGCMsgWebAPIJobRequestForwardResponse
//
message CMsgGCMsgWebAPIJobRequestForwardResponse
{
optional uint32 dir_index = 1; // The directory index of the GC which has been delegated to handle this particular WebAPI request
}
//
// k_EGCMsgGetPurchaseTrustStatus
//
message CGCSystemMsg_GetPurchaseTrust_Request
{
optional fixed64 steamid = 1; // User to get details for
}
message CGCSystemMsg_GetPurchaseTrust_Response
{
optional bool has_prior_purchase_history = 1; // The user has prior purchase history with no recent gaps in purchase activity
optional bool has_no_recent_password_resets = 2; // The user hasn't had their password reset recently
optional bool is_wallet_cash_trusted = 3; // False if the user has recently used a new payment method to fund his or her wallet
optional uint32 time_all_trusted = 4; // The time that the user will be trusted in all of the given fields if he or she were to complete a microtransaction right now
}
// k_EMsgGCHAccountVacStatusChange
message CMsgGCHAccountVacStatusChange
{
optional fixed64 steam_id = 1;
optional uint32 app_id = 2;
optional uint32 rtime_vacban_starts = 3;
optional bool is_banned_now = 4;
optional bool is_banned_future = 5;
}
//
// k_EGCMsgGetPartnerAccountLink
//
message CMsgGCGetPartnerAccountLink
{
optional fixed64 steamid = 1; // User whose partner account link details we want to get
}
message CMsgGCGetPartnerAccountLink_Response
{
optional uint32 pwid = 1; // Perfect World ID (not specified if not linked)
optional uint32 nexonid = 2; // Nexon ID (not specified if not linked)
}
//
// k_EGCMsgMasterSetWebAPIRouting and k_EGCMsgMasterSetClientMsgRouting
//
message CMsgGCRoutingInfo
{
enum RoutingMethod
{
RANDOM = 0; // random instead of round-robin so that we don't need to track state per routing pool
DISCARD = 1;
CLIENT_STEAMID = 2;
PROTOBUF_FIELD_UINT64 = 3;
WEBAPI_PARAM_UINT64 = 4;
}
repeated uint32 dir_index = 1; // One or more directory indices which are potential targets for this route
optional RoutingMethod method = 2 [ default = RANDOM ]; // Method by which the route choses its target from multiple dir_index values
optional RoutingMethod fallback = 3 [ default = DISCARD ]; // Fallback method to use when default method is not applicable (eg, field can't be parsed)
optional uint32 protobuf_field = 4; // For PROTOBUF_FIELD_UINT64, the protobuf field number to decode as a uint64 for routing
optional string webapi_param = 5; // For WEBAPI_PARAM_UINT64 method, the case-insensitive name of the webapi parameter
}
message CMsgGCMsgMasterSetWebAPIRouting
{
message Entry
{
optional string interface_name = 1;
optional string method_name = 2;
optional CMsgGCRoutingInfo routing = 3;
}
repeated Entry entries = 1;
}
message CMsgGCMsgMasterSetClientMsgRouting
{
message Entry
{
optional uint32 msg_type = 1; // Client message ID to be routed; top bit is ignored for historical reasons
optional CMsgGCRoutingInfo routing = 2;
}
repeated Entry entries = 1;
}
message CMsgGCMsgMasterSetWebAPIRouting_Response
{
optional int32 eresult = 1 [ default = 2 ]; // Success or failure code from the GCH when processing k_EGCMsgMasterSetWebAPIRouting
}
message CMsgGCMsgMasterSetClientMsgRouting_Response
{
optional int32 eresult = 1 [ default = 2 ]; // Success or failure code from the GCH when processing k_EGCMsgMasterSetClientMsgRouting
}
// k_EGCMsgSetOptions
message CMsgGCMsgSetOptions
{
enum Option
{
// Notifications (aka "data streams" - unsoliticed messages from Steam) - default disabled, specify to opt-in
NOTIFY_USER_SESSIONS = 0;
NOTIFY_SERVER_SESSIONS = 1;
NOTIFY_ACHIEVEMENTS = 2;
NOTIFY_VAC_ACTION = 3;
// todo: other options? should start options higher up, like 20+, to save room for streams?
}
repeated Option options = 1;
// The client_msg_ranges field indicates which client messages, if any, this GC should receive copies of
message MessageRange
{
required uint32 low = 1;
required uint32 high = 2;
}
repeated MessageRange client_msg_ranges = 2;
}
// k_EMsgGCHUpdateSession
message CMsgGCHUpdateSession
{
optional fixed64 steam_id = 1;
optional uint32 app_id = 2;
optional bool online = 3;
optional fixed64 server_steam_id = 4; // For a game client, the steam_id of the current server
optional uint32 server_addr = 5; // The IP address of the current server (or self for server state)
optional uint32 server_port = 6; // The IP port of the server (or self for server state)
optional uint32 os_type = 7;
optional uint32 client_addr = 8; // For a game client, the public IP address of the client
message ExtraField
{
optional string name = 1;
optional string value = 2;
}
repeated ExtraField extra_fields = 9;
optional fixed64 owner_id = 10; // owner Steam ID, different from player ID if borrowed
optional uint32 cm_session_sysid = 11; // routing info for client session that is playing the game
optional uint32 cm_session_identifier = 12;
repeated uint32 depot_ids = 13; // list of depots in packages that confer app, if requested k_EGCStreamDepots
}
//
// k_EGCMsgVSReportedSuspiciousActivity
//
message CMsgNotificationOfSuspiciousActivity
{
optional fixed64 steamid = 1;
optional uint32 appid = 2;
message MultipleGameInstances
{
optional uint32 app_instance_count = 1;
repeated fixed64 other_steamids = 2;
}
optional MultipleGameInstances multiple_instances = 3;
}
// Do not remove this comment due to a bug on the Mac OS X protobuf compiler

View File

@@ -0,0 +1,13 @@
$MacroRequired PROTOBUF_BUILDER_INCLUDED
$Project
{
$Folder "Protobuf Files"
{
$File "$SRCDIR/gcsdk/steammessages.proto"
$Folder "Generated Files"
{
$DynamicFile "$GENERATED_PROTO_DIR/steammessages.pb.h"
}
}
}