initial
This commit is contained in:
448
matchmaking/steam_lobbyapi.cpp
Normal file
448
matchmaking/steam_lobbyapi.cpp
Normal file
@@ -0,0 +1,448 @@
|
||||
//===== Copyright <20> 1996-2009, Valve Corporation, All rights reserved. ======//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//===========================================================================//
|
||||
|
||||
#include "mm_framework.h"
|
||||
|
||||
#include "fmtstr.h"
|
||||
|
||||
// memdbgon must be the last include file in a .cpp file!!!
|
||||
#include "tier0/memdbgon.h"
|
||||
|
||||
#if !defined( NO_STEAM ) && !defined( SWDS )
|
||||
|
||||
CInterlockedInt g_numSteamLeaderboardWriters;
|
||||
|
||||
|
||||
class CSteamLeaderboardWriter
|
||||
{
|
||||
public:
|
||||
CSteamLeaderboardWriter( KeyValues *pViewDescription, KeyValues *pViewData );
|
||||
~CSteamLeaderboardWriter();
|
||||
|
||||
protected:
|
||||
void UploadScore(SteamLeaderboard_t leaderboardHandle);
|
||||
|
||||
protected:
|
||||
CCallResult< CSteamLeaderboardWriter, LeaderboardFindResult_t > m_CallbackOnLeaderboardFindResult;
|
||||
void Steam_OnLeaderboardFindResult( LeaderboardFindResult_t *p, bool bError );
|
||||
|
||||
CCallResult< CSteamLeaderboardWriter, LeaderboardScoreUploaded_t > m_CallbackOnLeaderboardScoreUploaded;
|
||||
void Steam_OnLeaderboardScoreUploaded( LeaderboardScoreUploaded_t *p, bool bError );
|
||||
|
||||
CCallResult< CSteamLeaderboardWriter, LeaderboardScoresDownloaded_t > m_CallbackOnLeaderboardScoresDownloaded;
|
||||
void Steam_OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *p, bool bError );
|
||||
|
||||
protected:
|
||||
KeyValues *m_pViewDescription;
|
||||
KeyValues *m_pViewData;
|
||||
|
||||
int m_nViewDescriptionPayloadFormatSize;
|
||||
};
|
||||
|
||||
CSteamLeaderboardWriter::CSteamLeaderboardWriter( KeyValues *pViewDescription, KeyValues *pViewData ) :
|
||||
m_pViewDescription( pViewDescription->MakeCopy() ),
|
||||
m_pViewData( pViewData->MakeCopy() ),
|
||||
m_nViewDescriptionPayloadFormatSize( 0 )
|
||||
{
|
||||
SteamAPICall_t hCall;
|
||||
|
||||
if ( m_pViewDescription->GetBool( ":nocreate" ) )
|
||||
hCall = steamapicontext->SteamUserStats()->FindLeaderboard( m_pViewData->GetName() );
|
||||
else
|
||||
hCall = steamapicontext->SteamUserStats()->FindOrCreateLeaderboard(
|
||||
m_pViewData->GetName(),
|
||||
( ELeaderboardSortMethod ) m_pViewDescription->GetInt( ":sort" ),
|
||||
( ELeaderboardDisplayType ) m_pViewDescription->GetInt( ":format" ) );
|
||||
|
||||
m_CallbackOnLeaderboardFindResult.Set( hCall, this, &CSteamLeaderboardWriter::Steam_OnLeaderboardFindResult );
|
||||
|
||||
++ g_numSteamLeaderboardWriters;
|
||||
}
|
||||
|
||||
void CSteamLeaderboardWriter::Steam_OnLeaderboardFindResult( LeaderboardFindResult_t *p, bool bError )
|
||||
{
|
||||
if ( bError )
|
||||
{
|
||||
Warning( "Failed to contact leaderboard server for '%s'\n", m_pViewData->GetName() );
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !p->m_bLeaderboardFound )
|
||||
{
|
||||
DevWarning( "Leaderboard '%s' was not found on server\n", m_pViewData->GetName() );
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the view description contains the keyvalue ":sumscore 1" or anything in the ":payloadformat"
|
||||
// section containes ":upload sum" then we want to treat the score in the view data as a delta of
|
||||
// the data that is currently uploaded to the scoreboard. So first we have to read the data, then
|
||||
// aggregate the values. Also compute the payload size.
|
||||
bool bHasSum = m_pViewDescription->GetBool( ":scoresum" );
|
||||
bHasSum = bHasSum || m_pViewDescription->GetString( ":scoreformula", NULL );
|
||||
int nPayloadSize = 0;
|
||||
|
||||
KeyValues *pPayloadFormat = m_pViewDescription->FindKey( ":payloadformat" );
|
||||
|
||||
if ( pPayloadFormat )
|
||||
{
|
||||
// Iterate over each payload entry to see if any of them need to be summed.
|
||||
for ( int payloadIndex=0; ; ++payloadIndex )
|
||||
{
|
||||
KeyValues *pPayload = pPayloadFormat->FindKey( CFmtStr( "payload%d", payloadIndex ) );
|
||||
if ( !pPayload )
|
||||
{
|
||||
// No more payload entries specified.
|
||||
break;
|
||||
}
|
||||
|
||||
const char* pszFormat = pPayload->GetString( ":format", NULL );
|
||||
if ( V_stricmp( pszFormat, "int" ) == 0 )
|
||||
{
|
||||
nPayloadSize += sizeof( int );
|
||||
}
|
||||
else if ( V_stricmp( pszFormat, "uint64" ) == 0 )
|
||||
{
|
||||
nPayloadSize += sizeof( uint64 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Leaderboard description '%s' contains an invalid payload :format '%s'", m_pViewData->GetName(), pPayload->GetName() );
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
const char* pszUpload = pPayload->GetString( ":upload", NULL );
|
||||
if ( !pszUpload || V_stricmp( pszUpload, "last" ) == 0 )
|
||||
{
|
||||
// just overwrite the leaderboard value with our current value
|
||||
}
|
||||
else if ( V_stricmp( pszUpload, "sum" ) == 0 )
|
||||
{
|
||||
bHasSum = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Leaderboard description '%s' contains an invalid payload :format '%s'", m_pViewData->GetName(), pPayload->GetName() );
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_nViewDescriptionPayloadFormatSize = nPayloadSize;
|
||||
}
|
||||
|
||||
if ( bHasSum )
|
||||
{
|
||||
CSteamID steamID = steamapicontext->SteamUser()->GetSteamID();
|
||||
|
||||
// We need to download this user's current leaderboard data first.
|
||||
DevMsg( "Downloading score for leaderboard '%s', steam id '%llu'...\n", m_pViewData->GetName(), steamID.ConvertToUint64() );
|
||||
SteamAPICall_t hCall = steamapicontext->SteamUserStats()->DownloadLeaderboardEntriesForUsers( p->m_hSteamLeaderboard, &steamID, 1 );
|
||||
m_CallbackOnLeaderboardScoresDownloaded.Set( hCall, this, &CSteamLeaderboardWriter::Steam_OnLeaderboardScoresDownloaded );
|
||||
}
|
||||
else
|
||||
{
|
||||
UploadScore( p->m_hSteamLeaderboard );
|
||||
}
|
||||
}
|
||||
|
||||
void CSteamLeaderboardWriter::Steam_OnLeaderboardScoreUploaded( LeaderboardScoreUploaded_t *p, bool bError )
|
||||
{
|
||||
if ( bError )
|
||||
{
|
||||
Warning( "Failed to upload leaderboard score for '%s'\n", m_pViewData->GetName() );
|
||||
}
|
||||
else if ( !p->m_bSuccess )
|
||||
{
|
||||
Warning( "Failed to update leaderboard score for '%s'\n", m_pViewData->GetName() );
|
||||
}
|
||||
else if ( !p->m_bScoreChanged )
|
||||
{
|
||||
DevMsg( "Leaderboard score uploaded, but not changed for '%s'\n", m_pViewData->GetName() );
|
||||
}
|
||||
else
|
||||
{
|
||||
DevMsg( "Leaderboard score uploaded for '%s', new rank %d, old rank %d\n", m_pViewData->GetName(), p->m_nGlobalRankNew, p->m_nGlobalRankPrevious );
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CSteamLeaderboardWriter::Steam_OnLeaderboardScoresDownloaded( LeaderboardScoresDownloaded_t *p, bool bError )
|
||||
{
|
||||
if ( bError )
|
||||
{
|
||||
Warning( "Failed to download leaderboard score for '%s'\n", m_pViewData->GetName() );
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that we have the downloaded leaderboard data, we need to extract it, and sum the appropriate values
|
||||
// then write the data back out.
|
||||
if ( p->m_cEntryCount == 1 )
|
||||
{
|
||||
DevMsg( "Parsing downloaded scores for '%s'\n", m_pViewData->GetName() );
|
||||
|
||||
// We have the one entry we were looking for, so extract the current data from it.
|
||||
LeaderboardEntry_t leaderboardEntry;
|
||||
int32 *pPayloadData = new int32[m_nViewDescriptionPayloadFormatSize];
|
||||
if ( steamapicontext->SteamUserStats()->GetDownloadedLeaderboardEntry( p->m_hSteamLeaderboardEntries, 0, &leaderboardEntry, pPayloadData, m_nViewDescriptionPayloadFormatSize ) )
|
||||
{
|
||||
unsigned char *pCurrentPayload = (unsigned char*)pPayloadData;
|
||||
|
||||
// If the ranked score should be summed with the current ranked score from the leaderboard entry,
|
||||
// do that now.
|
||||
if ( m_pViewDescription->GetBool( ":scoresum" ) )
|
||||
{
|
||||
const char *pScore = m_pViewDescription->GetString( ":score", NULL );
|
||||
m_pViewData->SetInt( pScore, leaderboardEntry.m_nScore + m_pViewData->GetInt( pScore ) );
|
||||
}
|
||||
|
||||
// Iterate over payload item and perform the appropriate aggregation method, then write the result
|
||||
// to the view data.
|
||||
// Iterate over all of the payload format information and extract the corresponding data from the view data.
|
||||
for ( int payloadIndex=0; ; ++payloadIndex )
|
||||
{
|
||||
KeyValues *pPayload = m_pViewDescription->FindKey( CFmtStr( ":payloadformat/payload%d", payloadIndex ) );
|
||||
if ( !pPayload )
|
||||
{
|
||||
// No more payload entries specified.
|
||||
break;
|
||||
}
|
||||
|
||||
// Get payload format, aggregate it if necessary, then advance the current payload pointer.
|
||||
const char *pFormat = pPayload->GetString( ":format", NULL );
|
||||
const char *pScore = pPayload->GetString( ":score", NULL );
|
||||
const char *pUpload = pPayload->GetString( ":upload", NULL );
|
||||
|
||||
if ( V_stricmp( pFormat, "int" ) == 0 )
|
||||
{
|
||||
if ( pUpload && V_stricmp( pUpload, "sum" ) == 0 )
|
||||
{
|
||||
int score = *(int*)pCurrentPayload;
|
||||
m_pViewData->SetInt( pScore, score + m_pViewData->GetInt( pScore ) );
|
||||
}
|
||||
|
||||
pCurrentPayload += sizeof( int );
|
||||
}
|
||||
else if ( V_stricmp( pFormat, "uint64" ) == 0 )
|
||||
{
|
||||
if ( pUpload && V_stricmp( pUpload, "sum" ) == 0 )
|
||||
{
|
||||
uint64 score = *(uint64*)pCurrentPayload;
|
||||
m_pViewData->SetUint64( pScore, score + m_pViewData->GetUint64( pScore ) );
|
||||
}
|
||||
|
||||
pCurrentPayload += sizeof( uint64 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Leaderboard description '%s' contains an invalid payload :format '%s'", m_pViewData->GetName(), pPayload->GetName() );
|
||||
delete [] pPayloadData;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've aggregated all of the data, we need to check to see if the ranking data
|
||||
// (the data associated with ":score") needs to have a forumla applied to it.
|
||||
const char* pszScoreFormula = m_pViewDescription->GetString( ":scoreformula", NULL );
|
||||
if ( pszScoreFormula )
|
||||
{
|
||||
// Create an expression from the string.
|
||||
CExpressionCalculator calc( pszScoreFormula );
|
||||
|
||||
// Set variables that correspond to each payload.
|
||||
for ( int payloadIndex=0; ; ++payloadIndex )
|
||||
{
|
||||
CFmtStr payloadName = CFmtStr( "payload%d", payloadIndex );
|
||||
KeyValues *pPayload = m_pViewDescription->FindKey( CFmtStr( ":payloadformat/%s", payloadName.Access() ) );
|
||||
if ( !pPayload )
|
||||
{
|
||||
// No more payload entries specified.
|
||||
break;
|
||||
}
|
||||
|
||||
// Get payload format and score.
|
||||
const char *pFormat = pPayload->GetString( ":format", NULL );
|
||||
const char *pScore = pPayload->GetString( ":score", NULL );
|
||||
|
||||
if ( V_stricmp( pFormat, "int" ) == 0 )
|
||||
{
|
||||
int value = m_pViewData->GetInt( pScore );
|
||||
calc.SetVariable( payloadName, value );
|
||||
}
|
||||
else if ( V_stricmp( pFormat, "uint64" ) == 0 )
|
||||
{
|
||||
uint64 value = m_pViewData->GetUint64( pScore );
|
||||
calc.SetVariable( payloadName, value );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Leaderboard description '%s' contains an invalid payload :format '%s'", m_pViewData->GetName(), pPayload->GetName() );
|
||||
delete [] pPayloadData;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the expression and apply it to our view data.
|
||||
float value = 0.0f;
|
||||
if ( calc.Evaluate( value ) )
|
||||
{
|
||||
m_pViewData->SetInt( m_pViewDescription->GetString( ":score", NULL ), (int)value );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Failed to evaluate leaderboard expression.\n"
|
||||
"\tLeaderboard = %s\n"
|
||||
"\tExpression: %s\n",
|
||||
m_pViewData->GetName(),
|
||||
pszScoreFormula
|
||||
);
|
||||
delete [] pPayloadData;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] pPayloadData;
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Failed to download leaderboard score for '%s'\n", m_pViewData->GetName() );
|
||||
delete [] pPayloadData;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UploadScore( p->m_hSteamLeaderboard );
|
||||
}
|
||||
|
||||
void CSteamLeaderboardWriter::UploadScore(SteamLeaderboard_t leaderboardHandle)
|
||||
{
|
||||
unsigned char *pvPayloadPtr = (unsigned char *)m_pViewData->GetPtr( ":payloadptr" );
|
||||
int nPayloadSize = m_pViewData->GetInt( ":payloadsize" );
|
||||
|
||||
// If the view description contains ":payloadformat", then we need to construct the payload pointer
|
||||
// based on that description and the view data.
|
||||
KeyValues *pPayloadFormat = m_pViewDescription->FindKey( ":payloadformat" );
|
||||
if ( pPayloadFormat )
|
||||
{
|
||||
if ( pvPayloadPtr )
|
||||
{
|
||||
Warning( "Leaderboard data and description '%s' contain both :payloadptr and :payloadformat.\n", m_pViewData->GetName() );
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate a buffer for the payload.
|
||||
if ( m_nViewDescriptionPayloadFormatSize > 0 )
|
||||
{
|
||||
nPayloadSize = m_nViewDescriptionPayloadFormatSize;
|
||||
pvPayloadPtr = new unsigned char[nPayloadSize];
|
||||
|
||||
unsigned char *pvCurPayloadPtr = (unsigned char*)pvPayloadPtr;
|
||||
memset( pvCurPayloadPtr, 0, nPayloadSize );
|
||||
|
||||
// Iterate over all of the payload format information and extract the corresponding data from the view data.
|
||||
for ( int payloadIndex=0; ; ++payloadIndex )
|
||||
{
|
||||
KeyValues *pPayload = pPayloadFormat->FindKey( CFmtStr( "payload%d", payloadIndex ) );
|
||||
if ( !pPayload )
|
||||
{
|
||||
// No more payload entries specified.
|
||||
break;
|
||||
}
|
||||
|
||||
// Add assert to make sure payload pointer is still in bounds.
|
||||
|
||||
// Get the appropriate entry from the view data and write it to our payload buffer.
|
||||
const char* pszFormat = pPayload->GetString( ":format", NULL );
|
||||
const char* pszScore = pPayload->GetString( ":score", NULL );
|
||||
if ( V_stricmp( pszFormat, "int" ) == 0 )
|
||||
{
|
||||
int score = m_pViewData->GetInt( pszScore );
|
||||
#if defined( PLAT_BIG_ENDIAN )
|
||||
// On big-endian platforms, byteswap our scores so we always write the online data as little endian
|
||||
*(int*)pvCurPayloadPtr = DWordSwap( score );
|
||||
#else
|
||||
*(int*)pvCurPayloadPtr = score;
|
||||
#endif
|
||||
pvCurPayloadPtr += sizeof( int );
|
||||
}
|
||||
else if ( V_stricmp( pszFormat, "uint64" ) == 0 )
|
||||
{
|
||||
uint64 score = m_pViewData->GetUint64( pszScore );
|
||||
#if defined( PLAT_BIG_ENDIAN )
|
||||
// On big-endian platforms, byteswap our scores so we always write the online data as little endian
|
||||
*(int*)pvCurPayloadPtr = QWordSwap( score );
|
||||
#else
|
||||
*(uint64*)pvCurPayloadPtr = score;
|
||||
#endif
|
||||
pvCurPayloadPtr += sizeof( uint64 );
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning( "Leaderboard description '%s' contains an invalid payload :format '%s'", m_pViewData->GetName(), pPayload->GetName() );
|
||||
delete [] pvPayloadPtr;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add our constructed payload and the size to the view data.
|
||||
m_pViewData->SetPtr( ":payloadptr", pvPayloadPtr );
|
||||
m_pViewData->SetInt( ":payloadsize", nPayloadSize );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Upload score
|
||||
//
|
||||
int32 nScore = 0;
|
||||
nScore = ( uint32 ) m_pViewData->GetUint64( m_pViewDescription->GetString( ":score" ) );
|
||||
|
||||
DevMsg( "Uploading score for leaderboard '%s'...\n", m_pViewData->GetName() );
|
||||
KeyValuesDumpAsDevMsg( m_pViewData, 1 );
|
||||
|
||||
SteamAPICall_t hCall = steamapicontext->SteamUserStats()->UploadLeaderboardScore(
|
||||
leaderboardHandle,
|
||||
( ELeaderboardUploadScoreMethod ) m_pViewDescription->GetInt( ":upload" ),
|
||||
nScore,
|
||||
( int32 const *) pvPayloadPtr,
|
||||
( nPayloadSize + sizeof( int32 ) - 1 ) / sizeof( int32 ) );
|
||||
m_CallbackOnLeaderboardScoreUploaded.Set( hCall, this, &CSteamLeaderboardWriter::Steam_OnLeaderboardScoreUploaded );
|
||||
}
|
||||
|
||||
CSteamLeaderboardWriter::~CSteamLeaderboardWriter()
|
||||
{
|
||||
// We need to delete leaderboard payload allocated by caller
|
||||
delete [] ( char * ) m_pViewData->GetPtr( ":payloadptr" );
|
||||
|
||||
if ( m_pViewDescription )
|
||||
m_pViewDescription->deleteThis();
|
||||
|
||||
if ( m_pViewData )
|
||||
m_pViewData->deleteThis();
|
||||
|
||||
-- g_numSteamLeaderboardWriters;
|
||||
}
|
||||
|
||||
void Steam_WriteLeaderboardData( KeyValues *pViewDescription, KeyValues *pViewData )
|
||||
{
|
||||
MEM_ALLOC_CREDIT();
|
||||
|
||||
// CSteamLeaderboardWriter is driven by Steam callbacks and will
|
||||
// delete itself when finished
|
||||
new CSteamLeaderboardWriter( pViewDescription, pViewData );
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user