5503 lines
161 KiB
C++
5503 lines
161 KiB
C++
![]() |
//
|
||
|
// StatsDataMgr.cpp
|
||
|
//
|
||
|
// Copyright (C) 1999-2009 Rockstar Games. All Rights Reserved.
|
||
|
//
|
||
|
|
||
|
// --- Include Files ------------------------------------------------------------
|
||
|
|
||
|
// C headers
|
||
|
|
||
|
// Rage headers
|
||
|
#include "parser/tree.h"
|
||
|
#include "parser/treenode.h"
|
||
|
#include "parser/manager.h"
|
||
|
#include "String/stringhash.h"
|
||
|
#include "Diag/output.h"
|
||
|
#include "System/param.h"
|
||
|
#include "data/callback.h"
|
||
|
#include "rline/profilestats/rlprofilestatscommon.h"
|
||
|
#include "script/thread.h"
|
||
|
|
||
|
#if __BANK
|
||
|
#include "bank/bkmgr.h"
|
||
|
#include "bank/bank.h"
|
||
|
#include "data/callback.h"
|
||
|
#endif // __BANK
|
||
|
|
||
|
// Framework headers
|
||
|
#include "fwsys/timer.h"
|
||
|
#include "streaming/streamingengine.h"
|
||
|
|
||
|
// Stats headers
|
||
|
#include "StatsDataMgr.h"
|
||
|
#include "Stats/StatsMgr.h"
|
||
|
#include "Stats/StatsUtils.h"
|
||
|
#include "Stats/stats_channel.h"
|
||
|
#include "Stats/StatsInterface.h"
|
||
|
|
||
|
// Game headers
|
||
|
#include "core/game.h"
|
||
|
#include "Scene/DataFileMgr.h"
|
||
|
#include "Text/TextFile.h"
|
||
|
#include "script/script_channel.h"
|
||
|
#include "script/script.h"
|
||
|
#include "Peds/PlayerInfo.h"
|
||
|
#include "event/EventGroup.h"
|
||
|
#include "event/EventScript.h"
|
||
|
#include "frontend/ContextMenu.h"
|
||
|
|
||
|
// network headers
|
||
|
#include "Network/NetworkInterface.h"
|
||
|
#include "Network/Live/livemanager.h"
|
||
|
#include "Network/Network.h"
|
||
|
#include "Network/players/NetGamePlayer.h"
|
||
|
#include "Network/Stats/NetworkStockMarketStats.h"
|
||
|
#include "Network/Live/NetworkTelemetry.h"
|
||
|
#include "Network/Shop/Catalog.h"
|
||
|
#include "Network/Cloud/Tunables.h"
|
||
|
#include "Network/Cloud/UserContentManager.h"
|
||
|
|
||
|
#include "text/TextConversion.h"
|
||
|
#include "Core/gamesessionstatemachine.h"
|
||
|
#include "script/commands_stats.h"
|
||
|
#include "SaveLoad/GenericGameStorage.h"
|
||
|
|
||
|
#if RSG_DURANGO
|
||
|
#include "Network/Live/Events_durango.h"
|
||
|
#endif
|
||
|
|
||
|
#if __ASSERT
|
||
|
#include "script/thread.h"
|
||
|
#endif
|
||
|
|
||
|
#if __NET_SHOP_ACTIVE
|
||
|
#include "network/Shop/GameTransactionsSessionMgr.h"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#define __TEST_MASKED_STAT (0 && __DEV)
|
||
|
|
||
|
FRONTEND_STATS_OPTIMISATIONS()
|
||
|
SAVEGAME_OPTIMISATIONS()
|
||
|
|
||
|
|
||
|
#if __BANK
|
||
|
bool sStatData::ms_obfuscationDisabled = false;
|
||
|
#endif // __BANK
|
||
|
|
||
|
// --- Defines ------------------------------------------------------------------
|
||
|
RAGE_DEFINE_SUBCHANNEL(stat, profilestat, DIAG_SEVERITY_DEBUG3)
|
||
|
#define profileStatDebugf(fmt,...) RAGE_DEBUGF2(stat_profilestat,fmt,##__VA_ARGS__)
|
||
|
#define profileStatWarningf(fmt,...) RAGE_WARNINGF(stat_profilestat,fmt,##__VA_ARGS__)
|
||
|
#define profileStatErrorf(fmt,...) RAGE_ERRORF(stat_profilestat,fmt,##__VA_ARGS__)
|
||
|
|
||
|
#define NAMEDEF(id) {id, #id}
|
||
|
|
||
|
// The number of obfuscated stats we expect in the XML files - Used as a sanity check to make sure that
|
||
|
// level design don't add obfuscated stats, since they're very special purpose.
|
||
|
#if (RSG_ORBIS || RSG_PC || RSG_DURANGO)
|
||
|
static const u32 EXPECTED_NUM_OBFUSCATED_STATS = 137;
|
||
|
#else
|
||
|
static const u32 EXPECTED_NUM_OBFUSCATED_STATS = 13;
|
||
|
#endif // (RSG_ORBIS || RSG_PC || RSG_DURANGO)
|
||
|
|
||
|
static const unsigned CHECKDIRTYSTATS_INTERVAL = 2*60*1000; //interval between checking again for dirty stats
|
||
|
|
||
|
//Set to 1 to block multiplayer territory takeover data to be sent to ROS servers.
|
||
|
#define __BLOCK_TAKEOVERS ( 0 )
|
||
|
|
||
|
#if __BLOCK_TAKEOVERS
|
||
|
//If this command is present territory takeovers are enabled
|
||
|
PARAM(enabletakeovers, "Enable vterritory takeovers during multiplayer");
|
||
|
#endif // __BLOCK_TAKEOVERS
|
||
|
|
||
|
//Maintain a Dirty stat count to avoid search done if none are dirty.
|
||
|
static u32 s_DirtyStatsCount = 0;
|
||
|
//Similarly, maintain a count for profile stats dirtied
|
||
|
//This is incremented in response to
|
||
|
static u32 s_DirtyProfileStatsCount = 0;
|
||
|
|
||
|
//Similarly, maintain a count for savegame stats dirtied
|
||
|
//This is incremented in response to
|
||
|
static u32 s_DirtySavegameMPStatsCount = 0;
|
||
|
static u32 s_DirtySavegameSPStatsCount = 0;
|
||
|
PARAM(spewDirtySavegameStats, "[stat_savemgr] Maintain a Count of dirty savegame stats between saves.");
|
||
|
PARAM(disableStatObfuscation, "Disable memory obfuscarion for stats");
|
||
|
|
||
|
// --- Constants/Static arrays --------------------------------------------------
|
||
|
|
||
|
#if __ASSERT
|
||
|
// For Validation after the stats have been loaded.
|
||
|
StatId_static* StatId_static::sm_First = NULL;
|
||
|
StatId_char* StatId_char::sm_First = NULL;
|
||
|
#endif
|
||
|
|
||
|
struct sNameDef
|
||
|
{
|
||
|
u32 m_Id;
|
||
|
const char* m_Name;
|
||
|
};
|
||
|
|
||
|
// Keep in Sync with eFrontendStatType
|
||
|
static const sNameDef sStatsTypesNames[] =
|
||
|
{
|
||
|
NAMEDEF(STAT_TYPE_NONE),
|
||
|
NAMEDEF(STAT_TYPE_INT),
|
||
|
NAMEDEF(STAT_TYPE_FLOAT),
|
||
|
NAMEDEF(STAT_TYPE_STRING),
|
||
|
NAMEDEF(STAT_TYPE_BOOLEAN),
|
||
|
NAMEDEF(STAT_TYPE_UINT8),
|
||
|
NAMEDEF(STAT_TYPE_UINT16),
|
||
|
NAMEDEF(STAT_TYPE_UINT32),
|
||
|
NAMEDEF(STAT_TYPE_UINT64),
|
||
|
NAMEDEF(STAT_TYPE_TIME),
|
||
|
NAMEDEF(STAT_TYPE_CASH),
|
||
|
NAMEDEF(STAT_TYPE_PERCENT),
|
||
|
NAMEDEF(STAT_TYPE_DEGREES),
|
||
|
NAMEDEF(STAT_TYPE_WEIGHT),
|
||
|
NAMEDEF(STAT_TYPE_MILES),
|
||
|
NAMEDEF(STAT_TYPE_METERS),
|
||
|
NAMEDEF(STAT_TYPE_FEET),
|
||
|
NAMEDEF(STAT_TYPE_SECONDS),
|
||
|
NAMEDEF(STAT_TYPE_CHART),
|
||
|
NAMEDEF(STAT_TYPE_VELOCITY),
|
||
|
NAMEDEF(STAT_TYPE_DATE),
|
||
|
NAMEDEF(STAT_TYPE_POS),
|
||
|
NAMEDEF(STAT_TYPE_TEXTLABEL),
|
||
|
NAMEDEF(STAT_TYPE_PACKED),
|
||
|
NAMEDEF(STAT_TYPE_USERID),
|
||
|
NAMEDEF(STAT_TYPE_PROFILE_SETTING),
|
||
|
NAMEDEF(STAT_TYPE_INT64),
|
||
|
};
|
||
|
const int NUMBER_OF_STATS_TYPE_IDS = sizeof(sStatsTypesNames) / sizeof(sStatsTypesNames[0]);
|
||
|
|
||
|
// Keep in Sync with eFrontendStatShowFlag
|
||
|
static const sNameDef sStatsShowFlagsNames[] =
|
||
|
{
|
||
|
NAMEDEF(STAT_FLAG_ALWAYS_SHOW),
|
||
|
NAMEDEF(STAT_FLAG_INC_SHOW),
|
||
|
NAMEDEF(STAT_FLAG_NEVER_SHOW),
|
||
|
};
|
||
|
const int NUMBER_OF_STATS_SHOW_FLAGS = sizeof(sStatsShowFlagsNames) / sizeof(sStatsShowFlagsNames[0]);
|
||
|
|
||
|
// --- Structure/Class Implementation -------------------------------------------
|
||
|
|
||
|
int GET_NUMBER_OF_BITS_FROM_NAME(const char* name)
|
||
|
{
|
||
|
int numberOfBits = -1;
|
||
|
|
||
|
if (name)
|
||
|
{
|
||
|
if( stricmp(name, "int") == 0 )
|
||
|
{
|
||
|
numberOfBits = 8;
|
||
|
}
|
||
|
else if( stricmp(name, "bool") == 0 )
|
||
|
{
|
||
|
numberOfBits = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return numberOfBits;
|
||
|
}
|
||
|
|
||
|
int GET_TYPE_FROM_NAME(const char* name)
|
||
|
{
|
||
|
int tagType = STAT_TYPE_NONE;
|
||
|
|
||
|
if (statVerify(name))
|
||
|
{
|
||
|
if( stricmp(name, "int") == 0 || stricmp(name, "short") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_INT;
|
||
|
}
|
||
|
else if( stricmp(name, "float") == 0 || stricmp(name, "double") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_FLOAT;
|
||
|
}
|
||
|
else if( stricmp(name, "string") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_STRING;
|
||
|
}
|
||
|
else if( stricmp(name, "bool") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_BOOLEAN;
|
||
|
}
|
||
|
else if( stricmp(name, "u8") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_UINT8;
|
||
|
}
|
||
|
else if( stricmp(name, "u16") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_UINT16;
|
||
|
}
|
||
|
else if( stricmp(name, "u32") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_UINT32;
|
||
|
}
|
||
|
else if( stricmp(name, "u64") == 0 || stricmp(name, "x64") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_UINT64;
|
||
|
}
|
||
|
else if ( stricmp(name, "date") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_DATE;
|
||
|
}
|
||
|
else if ( stricmp(name, "pos") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_POS;
|
||
|
}
|
||
|
else if( stricmp(name, "label") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_TEXTLABEL;
|
||
|
}
|
||
|
else if( stricmp(name, "packed") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_PACKED;
|
||
|
}
|
||
|
else if( stricmp(name, "userid") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_USERID;
|
||
|
}
|
||
|
else if( stricmp(name, "profileSetting") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_PROFILE_SETTING;
|
||
|
}
|
||
|
else if( stricmp(name, "s64") == 0 || stricmp(name, "long") == 0 )
|
||
|
{
|
||
|
tagType = STAT_TYPE_INT64;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return tagType;
|
||
|
}
|
||
|
|
||
|
|
||
|
// --- sStatDescription ----------------------------------------------------------
|
||
|
|
||
|
void
|
||
|
sStatDescription::SetDirty(const bool value, const bool bDirtyProfile)
|
||
|
{
|
||
|
if (value && !m_Desc.as_SinglePlayer.m_Dirty)
|
||
|
{
|
||
|
s_DirtyStatsCount++;
|
||
|
}
|
||
|
else if (!value && m_Desc.as_SinglePlayer.m_Dirty)
|
||
|
{
|
||
|
statAssert(0 != s_DirtyStatsCount);
|
||
|
s_DirtyStatsCount--;
|
||
|
}
|
||
|
|
||
|
m_Desc.as_SinglePlayer.m_Dirty = value;
|
||
|
|
||
|
if (bDirtyProfile)
|
||
|
{
|
||
|
SetDirtyProfile(value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sStatDescription::SetDirtyProfile(const bool value)
|
||
|
{
|
||
|
if (value && StatsInterface::GetBlockSaveRequests())
|
||
|
return;
|
||
|
|
||
|
if (value && !m_Desc.as_SinglePlayer.m_DirtyProfile)
|
||
|
{
|
||
|
s_DirtyProfileStatsCount++;
|
||
|
}
|
||
|
else if (!value && m_Desc.as_SinglePlayer.m_DirtyProfile)
|
||
|
{
|
||
|
statAssert(0 != s_DirtyProfileStatsCount);
|
||
|
s_DirtyProfileStatsCount--;
|
||
|
}
|
||
|
|
||
|
m_Desc.as_SinglePlayer.m_DirtyProfile = value;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sStatDescription::SetDirtySavegame(const bool value)
|
||
|
{
|
||
|
if (PARAM_spewDirtySavegameStats.Get())
|
||
|
{
|
||
|
if (GetServerAuthoritative())
|
||
|
return;
|
||
|
|
||
|
if (GetIsCommunityStat())
|
||
|
return;
|
||
|
|
||
|
if (GetIsProfileSetting())
|
||
|
return;
|
||
|
|
||
|
if (value && !m_Desc.as_SinglePlayer.m_DirtySavegame)
|
||
|
{
|
||
|
if (m_Desc.as_SinglePlayer.m_OnlineStat)
|
||
|
{
|
||
|
s_DirtySavegameMPStatsCount++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
s_DirtySavegameSPStatsCount++;
|
||
|
}
|
||
|
}
|
||
|
else if (!value && m_Desc.as_SinglePlayer.m_DirtySavegame)
|
||
|
{
|
||
|
if (m_Desc.as_SinglePlayer.m_OnlineStat)
|
||
|
{
|
||
|
statAssert(0 != s_DirtySavegameMPStatsCount);
|
||
|
s_DirtySavegameMPStatsCount--;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statAssert(0 != s_DirtySavegameSPStatsCount);
|
||
|
s_DirtySavegameSPStatsCount--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_Desc.as_SinglePlayer.m_DirtySavegame = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sStatDescription::SetIsPlayerInventory( )
|
||
|
{
|
||
|
if (!statVerify(GetIsOnlineData()))
|
||
|
return false;
|
||
|
|
||
|
if (!statVerify(GetServerAuthoritative()))
|
||
|
return false;
|
||
|
|
||
|
m_Desc.as_MultiPlayer.m_PlayerInventory = true;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --- StatId_char ----------------------------------------------------------
|
||
|
|
||
|
#if __ASSERT
|
||
|
StatId_char::StatId_char(const char* name, const atHashString& sp0, const atHashString& sp1, const atHashString& sp2, const atHashString& mp0, const atHashString& mp1, const bool validate)
|
||
|
{
|
||
|
m_Validate = validate;
|
||
|
Set(name);
|
||
|
m_Id[CHAR_MICHEAL] = sp0;
|
||
|
m_Id[CHAR_FRANKLIN] = sp1;
|
||
|
m_Id[CHAR_TREVOR] = sp2;
|
||
|
m_Id[CHAR_MP0] = mp0;
|
||
|
m_Id[CHAR_MP1] = mp1;
|
||
|
}
|
||
|
#endif // __ASSERT
|
||
|
|
||
|
StatId_char::StatId_char(const atHashString& sp0, const atHashString& sp1, const atHashString& sp2, const atHashString& mp0, const atHashString& mp1)
|
||
|
{
|
||
|
ASSERT_ONLY(m_Validate = false;)
|
||
|
m_Id[CHAR_MICHEAL] = sp0;
|
||
|
m_Id[CHAR_FRANKLIN] = sp1;
|
||
|
m_Id[CHAR_TREVOR] = sp2;
|
||
|
m_Id[CHAR_MP0] = mp0;
|
||
|
m_Id[CHAR_MP1] = mp1;
|
||
|
}
|
||
|
|
||
|
#if __ASSERT
|
||
|
StatId_char::~StatId_char()
|
||
|
{
|
||
|
if (m_Validate)
|
||
|
{
|
||
|
statAssertf(0, "DONT - STATIC STAT ='%s'", m_Name);
|
||
|
}
|
||
|
}
|
||
|
#endif // __ASSERT
|
||
|
|
||
|
void StatId_char::Set(const char* str)
|
||
|
{
|
||
|
#if __ASSERT
|
||
|
if (m_Validate)
|
||
|
{
|
||
|
m_Next = sm_First;
|
||
|
sm_First = this;
|
||
|
m_Name = str;
|
||
|
statAssert(m_Name);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (int prefix=0; prefix<MAX_STATS_CHAR_INDEXES; prefix++)
|
||
|
{
|
||
|
char statString[MAX_STAT_LABEL_SIZE];
|
||
|
statString[0] = '\0';
|
||
|
|
||
|
if (str)
|
||
|
{
|
||
|
safecatf(statString,MAX_STAT_LABEL_SIZE,"%s_%s",s_StatsModelPrefixes[prefix],str);
|
||
|
}
|
||
|
|
||
|
m_Id[prefix] = str ? HASH_STAT_ID(statString) : 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
u32 StatId_char::GetHash(const u32 prefix) const
|
||
|
{
|
||
|
if(prefix < MAX_STATS_CHAR_INDEXES)
|
||
|
{
|
||
|
return m_Id[prefix];
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
u32 StatId_char::GetHash() const
|
||
|
{
|
||
|
u32 prefix = StatsInterface::GetStatsModelPrefix();
|
||
|
if(prefix < MAX_STATS_CHAR_INDEXES)
|
||
|
{
|
||
|
return m_Id[prefix];
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
StatId StatId_char::GetStatId() const
|
||
|
{
|
||
|
u32 prefix = StatsInterface::GetStatsModelPrefix();
|
||
|
if(prefix < MAX_STATS_CHAR_INDEXES)
|
||
|
{
|
||
|
return StatId(m_Id[prefix]);
|
||
|
}
|
||
|
|
||
|
return StatId();
|
||
|
}
|
||
|
|
||
|
StatId StatId_char::GetOnlineStatId(const u32 slot) const
|
||
|
{
|
||
|
//const int slot = StatsInterface::GetIntStat(STAT_MPPLY_LAST_MP_CHAR) + CHAR_MP0;
|
||
|
if (statVerify(slot>=CHAR_MP0) && statVerify(slot<=CHAR_MP_END))
|
||
|
{
|
||
|
return StatId(m_Id[slot]);
|
||
|
}
|
||
|
|
||
|
return StatId();
|
||
|
}
|
||
|
|
||
|
// --- sStatData ----------------------------------------------------------
|
||
|
|
||
|
sStatData::sStatData(const char* UNUSED_PARAM(pName), sStatDescription& desc)
|
||
|
{
|
||
|
m_Desc = desc;
|
||
|
|
||
|
ASSERT_ONLY( m_CoderStat = true; )
|
||
|
|
||
|
NOTFINAL_ONLY( m_ShowChanges = false; )
|
||
|
NOTFINAL_ONLY( m_ShowOnScreen = false; )
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sStatData::Reset()
|
||
|
{
|
||
|
m_Desc.SetSynched(true);
|
||
|
m_Desc.SetForcedSynched(false);
|
||
|
m_Desc.SetDirtyInThisSession(false);
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sStatData::SetData(void* pData, const unsigned sizeofData, bool& retHaschangedValue)
|
||
|
{
|
||
|
retHaschangedValue = false;
|
||
|
|
||
|
bool ret = false;
|
||
|
|
||
|
if (statVerify(pData))
|
||
|
{
|
||
|
if (GetIsBaseType(STAT_TYPE_PROFILE_SETTING))
|
||
|
{
|
||
|
retHaschangedValue = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (sizeofData == GetSizeOfData() || ((GetIsBaseType(STAT_TYPE_STRING) || GetIsBaseType(STAT_TYPE_USERID)) && sizeofData <= GetSizeOfData()))
|
||
|
{
|
||
|
ret = true;
|
||
|
|
||
|
//Value is not different.
|
||
|
if (ValueIsEqual(pData))
|
||
|
return ret;
|
||
|
|
||
|
retHaschangedValue = true;
|
||
|
|
||
|
switch (GetType())
|
||
|
{
|
||
|
case STAT_TYPE_TEXTLABEL: SetInt(*((int*)pData)); break;
|
||
|
case STAT_TYPE_INT: SetInt(*((int*)pData)); break;
|
||
|
case STAT_TYPE_INT64: SetInt64(*((s64*)pData)); break;
|
||
|
case STAT_TYPE_FLOAT: SetFloat(*((float*)pData)); break;
|
||
|
case STAT_TYPE_BOOLEAN: SetBoolean(*((bool*)pData)); break;
|
||
|
case STAT_TYPE_UINT8: SetUInt8(*((u8*)pData)); break;
|
||
|
case STAT_TYPE_UINT16: SetUInt16(*((u16*)pData)); break;
|
||
|
case STAT_TYPE_UINT32: SetUInt32(*((u32*)pData)); break;
|
||
|
case STAT_TYPE_UINT64: SetUInt64(*((u64*)pData)); break;
|
||
|
case STAT_TYPE_PACKED: SetUInt64(*((u64*)pData)); break;
|
||
|
case STAT_TYPE_DATE: SetUInt64(*((u64*)pData)); break;
|
||
|
case STAT_TYPE_POS: SetUInt64(*((u64*)pData)); break;
|
||
|
case STAT_TYPE_STRING: SetString((char*)pData); break;
|
||
|
case STAT_TYPE_USERID:
|
||
|
{
|
||
|
if (sizeof(u64) == sizeofData)
|
||
|
{
|
||
|
SetUInt64(*((u64*)pData));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SetUserId((char*)pData);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
retHaschangedValue = false;
|
||
|
ret = false;
|
||
|
statAssertf(0, "Invalid stat tag type \"%d\"", GetType());
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (retHaschangedValue)
|
||
|
{
|
||
|
DEV_ONLY(if (m_ShowChanges) ToString();)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sLabelStatData::SetInt(const int value)
|
||
|
{
|
||
|
sIntStatData::SetInt(value);
|
||
|
|
||
|
#if __BANK
|
||
|
m_Label[0] = '\0';
|
||
|
if (TheText.DoesTextLabelExist(GetInt()))
|
||
|
{
|
||
|
safecpy( m_Label, TheText.Get(GetInt(), "LABEL_STAT") );
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sUns64StatData::SetUInt64(const u64 value)
|
||
|
{
|
||
|
Encode(m_Value,value);
|
||
|
|
||
|
#if __BANK
|
||
|
m_ValueLo = (u32)value;
|
||
|
m_ValueHi = 0;
|
||
|
|
||
|
if (value > MAX_UINT32)
|
||
|
{
|
||
|
m_ValueLo = MAX_UINT32;
|
||
|
m_ValueHi = (u32)(value - MAX_UINT32);
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sPosStatData::SetUInt64(const u64 value)
|
||
|
{
|
||
|
Encode(m_Value,value);
|
||
|
BANK_ONLY( StatsInterface::UnPackPos(Decode(m_Value), m_Position.x, m_Position.y, m_Position.z); )
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sDateStatData::SetUInt64(const u64 value)
|
||
|
{
|
||
|
Encode(m_Value,value);
|
||
|
BANK_ONLY( StatsInterface::UnPackDate(Decode(m_Value), m_Date.m_year, m_Date.m_month, m_Date.m_day, m_Date.m_hour, m_Date.m_minutes, m_Date.m_seconds, m_Date.m_milliseconds); )
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sPackedStatData::SetUInt64(const u64 value)
|
||
|
{
|
||
|
Encode(m_Value,value);
|
||
|
BANK_ONLY( SetupWidget(); )
|
||
|
}
|
||
|
|
||
|
#if __BANK
|
||
|
|
||
|
void
|
||
|
sPackedStatData::SetupWidget()
|
||
|
{
|
||
|
if (NumberOfBitsIsValid())
|
||
|
{
|
||
|
u64 mask = (1 << m_NumberOfBits) - 1;
|
||
|
|
||
|
char temp[WIDGET_STRING_SIZE];
|
||
|
temp[0] = '\0';
|
||
|
m_WidgetValue[0] = '\0';
|
||
|
|
||
|
for (int i=0; i<64; i+=m_NumberOfBits)
|
||
|
{
|
||
|
u64 maskShifted = (mask << i);
|
||
|
int data = (int)((Decode(m_Value) & maskShifted) >> i);
|
||
|
|
||
|
temp[0] = '\0';
|
||
|
formatf(temp, "%s", m_WidgetValue);
|
||
|
|
||
|
if(i==0)
|
||
|
formatf(m_WidgetValue, "%d", data);
|
||
|
else
|
||
|
formatf(m_WidgetValue, "%s:%d", temp, data);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
|
||
|
bool
|
||
|
sUserIdStatData::IsDefaultValue() const
|
||
|
{
|
||
|
return (m_UserId == 0);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sUserIdStatData::Reset()
|
||
|
{
|
||
|
sStatData::Reset();
|
||
|
|
||
|
m_UserId = 0;
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (m_NextUserId)
|
||
|
{
|
||
|
m_NextUserId->Reset();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sUserIdStatData::ValueIsEqual(void* pData) const
|
||
|
{
|
||
|
if (pData)
|
||
|
{
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (m_NextUserId)
|
||
|
{
|
||
|
char buffer[128];
|
||
|
if (GetUserId(&buffer[0], 128))
|
||
|
{
|
||
|
return (stricmp(buffer, (char*)pData) == 0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//always update the value.
|
||
|
return false;
|
||
|
}
|
||
|
#elif RLGAMERHANDLE_SC || RLGAMERHANDLE_XBL
|
||
|
char buffer[128];
|
||
|
if (GetUserId(&buffer[0], 128))
|
||
|
{
|
||
|
return (stricmp(buffer, (char*)pData) == 0);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
unsigned
|
||
|
sUserIdStatData::GetSizeOfData() const
|
||
|
{
|
||
|
#if RLGAMERHANDLE_XBL
|
||
|
return 20;
|
||
|
#elif RLGAMERHANDLE_SC
|
||
|
return 20;
|
||
|
#elif RLGAMERHANDLE_NP
|
||
|
if (rlGamerHandle::IsUsingAccountIdAsKey())
|
||
|
{
|
||
|
return 20;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return 17; // 16+1 for the NULL terminating character
|
||
|
}
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sUserIdStatData::GetUserId(char* userId, const int bufSize) const
|
||
|
{
|
||
|
statAssertf(userId, "Null handle");
|
||
|
if (!userId)
|
||
|
return false;
|
||
|
|
||
|
Assert(userId);
|
||
|
Assert(bufSize >= GetSizeOfData());
|
||
|
|
||
|
userId[0] = '\0';
|
||
|
|
||
|
char tmpBuf[128] = {""};
|
||
|
|
||
|
#if RLGAMERHANDLE_XBL || RLGAMERHANDLE_SC
|
||
|
|
||
|
if(m_UserId != 0)
|
||
|
{
|
||
|
formatf(tmpBuf, sizeof(tmpBuf), "%" I64FMT "u", m_UserId);
|
||
|
}
|
||
|
#elif RLGAMERHANDLE_NP
|
||
|
|
||
|
if (rlGamerHandle::IsUsingAccountIdAsKey())
|
||
|
{
|
||
|
if (m_UserId == 0 && m_NextUserId && m_NextUserId->m_UserId != 0)
|
||
|
{
|
||
|
formatf(tmpBuf, sizeof(tmpBuf), "%" I64FMT "u", m_NextUserId->m_UserId);
|
||
|
}
|
||
|
}
|
||
|
else if(m_UserId != 0)
|
||
|
{
|
||
|
u32 shiftcounter = 0;
|
||
|
for (u32 i = 0; ((m_NextUserId && i < 16) || (!m_NextUserId && i < 8)); i++, shiftcounter++)
|
||
|
{
|
||
|
if (i == 8)
|
||
|
{
|
||
|
shiftcounter = 0;
|
||
|
}
|
||
|
u32 shift = shiftcounter * 8;
|
||
|
|
||
|
u64 mask = (1 << 8) - 1;
|
||
|
u64 maskShifted = (mask << shift);
|
||
|
|
||
|
int data = 0;
|
||
|
if (i < 8)
|
||
|
{
|
||
|
data = (int)((m_UserId & maskShifted) >> shift);
|
||
|
}
|
||
|
else if (statVerify(m_NextUserId))
|
||
|
{
|
||
|
data = (int)((m_NextUserId->m_UserId & maskShifted) >> shift);
|
||
|
}
|
||
|
statAssert(data <= 255);
|
||
|
|
||
|
tmpBuf[i] = static_cast<char>(data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#elif __WIN32PC || RSG_DURANGO
|
||
|
&bufSize;
|
||
|
#endif
|
||
|
|
||
|
if(rlVerifyf(strlen(tmpBuf) <= (size_t) bufSize, "Buffer too small: Required:%u, Size:%d", (unsigned)strlen(tmpBuf), bufSize))
|
||
|
{
|
||
|
safecpy(userId, tmpBuf, bufSize);
|
||
|
}
|
||
|
|
||
|
return (userId[0] != '\0') ? true : false;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sUserIdStatData::SetUserId(const char* userId)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
statAssertf(userId, "Null handle");
|
||
|
if (!userId)
|
||
|
return result;
|
||
|
|
||
|
//If the value being set is 'STAT_UNKNOWN'
|
||
|
// then it means we want to reset the user id.
|
||
|
if (stricmp("STAT_UNKNOWN", userId) == 0)
|
||
|
{
|
||
|
result = true;
|
||
|
|
||
|
m_UserId = 0;
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (m_NextUserId)
|
||
|
{
|
||
|
m_NextUserId->Reset();
|
||
|
}
|
||
|
#endif //RLGAMERHANDLE_NP
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
#if RLGAMERHANDLE_XBL || RLGAMERHANDLE_SC
|
||
|
|
||
|
m_UserId = 0;
|
||
|
const int ret = sscanf(userId, "%" I64FMT "u", &m_UserId);
|
||
|
result = (ret == 1);
|
||
|
statAssertf(result, "Improperly formed User Id string:\"%s\"", userId);
|
||
|
|
||
|
#elif RLGAMERHANDLE_NP
|
||
|
|
||
|
statAssertf(m_NextUserId, "Invalid stat missing storage for last 8 characters");
|
||
|
|
||
|
if (m_NextUserId)
|
||
|
{
|
||
|
m_UserId = 0;
|
||
|
m_NextUserId->m_UserId = 0;
|
||
|
|
||
|
if (rlGamerHandle::IsUsingAccountIdAsKey())
|
||
|
{
|
||
|
const int ret = sscanf(userId, "%" I64FMT "u", &m_NextUserId->m_UserId);
|
||
|
result = (ret == 1);
|
||
|
statAssertf(result, "Improperly formed User Id string:\"%s\"", userId);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = true;
|
||
|
|
||
|
const u32 bufSize = strlen(userId);
|
||
|
statAssert(bufSize <= 16);
|
||
|
|
||
|
u32 shiftcounter = 0;
|
||
|
for (u32 i = 0; i < bufSize; i++, shiftcounter++)
|
||
|
{
|
||
|
const u16 dataValue = static_cast<u16>(userId[i]);
|
||
|
|
||
|
if (i == 8)
|
||
|
{
|
||
|
shiftcounter = 0;
|
||
|
}
|
||
|
u32 shift = shiftcounter * 8;
|
||
|
|
||
|
u64 oldValue = 0;
|
||
|
if (i < 8)
|
||
|
oldValue = m_UserId;
|
||
|
else
|
||
|
oldValue = m_NextUserId->m_UserId;
|
||
|
|
||
|
u64 dataInU64 = ((u64)dataValue) << (shift); //Data shifted to the position it should be
|
||
|
u64 maskInU64 = ((u64)0xFF) << (shift); //Shifted to the position were we want the bits changed
|
||
|
|
||
|
oldValue = oldValue & ~maskInU64; //Old value with 0 on the bits we want to change
|
||
|
|
||
|
if (i < 8)
|
||
|
m_UserId = oldValue | dataInU64;
|
||
|
else
|
||
|
m_NextUserId->m_UserId = oldValue | dataInU64;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
statAssertf(result, " Unhandled online service:\"%s\"", userId);
|
||
|
#endif
|
||
|
|
||
|
#if __BANK
|
||
|
if (result)
|
||
|
{
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (m_NextUserId)
|
||
|
GetUserId(m_WidgetValue, 128);
|
||
|
#else
|
||
|
GetUserId(m_WidgetValue, 128);
|
||
|
#endif
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void sUserIdStatData::SetDirty(const bool bDirtyProfile)
|
||
|
{
|
||
|
sStatData::SetDirty(bDirtyProfile);
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if(m_NextUserId)
|
||
|
m_NextUserId->SetDirty(bDirtyProfile);
|
||
|
#endif // RLGAMERHANDLE_NP
|
||
|
}
|
||
|
|
||
|
void sUserIdStatData::ResynchServerAuthoritative()
|
||
|
{
|
||
|
sStatData::ResynchServerAuthoritative();
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if(m_NextUserId)
|
||
|
m_NextUserId->ResynchServerAuthoritative();
|
||
|
#endif // RLGAMERHANDLE_NP
|
||
|
}
|
||
|
|
||
|
|
||
|
bool
|
||
|
sProfileSettingStatData::GetProfileSetting(void* pData) const
|
||
|
{
|
||
|
if (statVerify(pData))
|
||
|
{
|
||
|
CProfileSettings& settings = CProfileSettings::GetInstance();
|
||
|
if(settings.AreSettingsValid() && settings.Exists((CProfileSettings::ProfileSettingId) m_profileId))
|
||
|
{
|
||
|
const int currvalue = settings.GetInt((CProfileSettings::ProfileSettingId) m_profileId);
|
||
|
|
||
|
sysMemCpy(pData, &currvalue, GetSizeOfData());
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
sProfileSettingStatData::GetInt() const
|
||
|
{
|
||
|
int currvalue = 0;
|
||
|
|
||
|
GetProfileSetting(&currvalue);
|
||
|
|
||
|
return currvalue;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sProfileSettingStatData::ValueIsEqual(void* pData) const
|
||
|
{
|
||
|
return pData ? GetInt() == *((int*)pData) : false;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sStatData::GetData(void* pData, const unsigned sizeofData) const
|
||
|
{
|
||
|
bool ret = false;
|
||
|
|
||
|
if (statVerify(pData))
|
||
|
{
|
||
|
if (GetIsBaseType(STAT_TYPE_PROFILE_SETTING) && statVerify(sizeofData == GetSizeOfData()))
|
||
|
{
|
||
|
return GetProfileSetting(pData);
|
||
|
}
|
||
|
|
||
|
if (statVerify(sizeofData == GetSizeOfData() || (GetType() == STAT_TYPE_USERID && (sizeofData >= GetSizeOfData() || sizeofData == sizeof(u64)))))
|
||
|
{
|
||
|
ret = true;
|
||
|
|
||
|
switch (GetType())
|
||
|
{
|
||
|
case STAT_TYPE_TEXTLABEL: { int value = GetInt(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_INT: { int value = GetInt(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_INT64: { s64 value = GetInt64(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_FLOAT: { float value = GetFloat(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_BOOLEAN: { bool value = GetBoolean(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_UINT8: { u8 value = GetUInt8(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_UINT16: { u16 value = GetUInt16(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_UINT32: { u32 value = GetUInt32(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_PACKED: { u64 value = GetUInt64(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_UINT64: { u64 value = GetUInt64(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_DATE: { u64 value = GetUInt64(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_POS: { u64 value = GetUInt64(); sysMemCpy(pData, &value, sizeofData); } break;
|
||
|
case STAT_TYPE_STRING: { formatf((char*)pData, sizeofData, GetString()); } break;
|
||
|
case STAT_TYPE_USERID:
|
||
|
{
|
||
|
if (sizeofData == sizeof(u64))
|
||
|
{
|
||
|
u64 value = GetUInt64();
|
||
|
sysMemCpy(pData, &value, sizeofData);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GetUserId((char*)pData, sizeofData);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
ret = false;
|
||
|
statAssertf(0, "Invalid stat tag type \"%d\"", GetType());
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (GetIsBaseType(STAT_TYPE_STRING))
|
||
|
{
|
||
|
statAssert(sizeofData <= MAX_STAT_STRING_SIZE);
|
||
|
statAssert(sizeofData >= strlen(GetString()));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sStatData::ExpectedProfileStatType(const rlProfileStatsValue* val) const
|
||
|
{
|
||
|
bool typeIsCorrect = false;
|
||
|
|
||
|
switch (GetType())
|
||
|
{
|
||
|
case STAT_TYPE_INT:
|
||
|
case STAT_TYPE_TIME:
|
||
|
case STAT_TYPE_CASH:
|
||
|
case STAT_TYPE_PERCENT:
|
||
|
case STAT_TYPE_DEGREES:
|
||
|
case STAT_TYPE_WEIGHT:
|
||
|
case STAT_TYPE_MILES:
|
||
|
case STAT_TYPE_METERS:
|
||
|
case STAT_TYPE_FEET:
|
||
|
case STAT_TYPE_SECONDS:
|
||
|
case STAT_TYPE_CHART:
|
||
|
case STAT_TYPE_VELOCITY:
|
||
|
case STAT_TYPE_TEXTLABEL:
|
||
|
case STAT_TYPE_BOOLEAN:
|
||
|
case STAT_TYPE_UINT8:
|
||
|
case STAT_TYPE_UINT16:
|
||
|
case STAT_TYPE_PROFILE_SETTING:
|
||
|
typeIsCorrect = (RL_PROFILESTATS_TYPE_INT32 == val->GetType());
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_UINT32:
|
||
|
case STAT_TYPE_UINT64:
|
||
|
case STAT_TYPE_POS:
|
||
|
case STAT_TYPE_DATE:
|
||
|
case STAT_TYPE_PACKED:
|
||
|
case STAT_TYPE_USERID:
|
||
|
case STAT_TYPE_INT64:
|
||
|
typeIsCorrect = (RL_PROFILESTATS_TYPE_INT64 == val->GetType());
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_FLOAT:
|
||
|
typeIsCorrect = (RL_PROFILESTATS_TYPE_FLOAT == val->GetType());
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_STRING:
|
||
|
default:
|
||
|
statAssert(0);
|
||
|
}
|
||
|
|
||
|
return typeIsCorrect;
|
||
|
}
|
||
|
|
||
|
rlProfileStatsType
|
||
|
sStatData::GetExpectedProfileStatType() const
|
||
|
{
|
||
|
rlProfileStatsType typeIsCorrect = RL_PROFILESTATS_TYPE_INVALID;
|
||
|
|
||
|
switch (GetType())
|
||
|
{
|
||
|
case STAT_TYPE_INT:
|
||
|
case STAT_TYPE_TIME:
|
||
|
case STAT_TYPE_CASH:
|
||
|
case STAT_TYPE_PERCENT:
|
||
|
case STAT_TYPE_DEGREES:
|
||
|
case STAT_TYPE_WEIGHT:
|
||
|
case STAT_TYPE_MILES:
|
||
|
case STAT_TYPE_METERS:
|
||
|
case STAT_TYPE_FEET:
|
||
|
case STAT_TYPE_SECONDS:
|
||
|
case STAT_TYPE_CHART:
|
||
|
case STAT_TYPE_VELOCITY:
|
||
|
case STAT_TYPE_TEXTLABEL:
|
||
|
case STAT_TYPE_BOOLEAN:
|
||
|
case STAT_TYPE_UINT8:
|
||
|
case STAT_TYPE_UINT16:
|
||
|
case STAT_TYPE_PROFILE_SETTING:
|
||
|
typeIsCorrect = RL_PROFILESTATS_TYPE_INT32;
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_UINT32:
|
||
|
case STAT_TYPE_UINT64:
|
||
|
case STAT_TYPE_DATE:
|
||
|
case STAT_TYPE_POS:
|
||
|
case STAT_TYPE_PACKED:
|
||
|
case STAT_TYPE_USERID:
|
||
|
case STAT_TYPE_INT64:
|
||
|
typeIsCorrect = RL_PROFILESTATS_TYPE_INT64;
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_FLOAT:
|
||
|
typeIsCorrect = RL_PROFILESTATS_TYPE_FLOAT;
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_STRING:
|
||
|
default:
|
||
|
statAssertf(false, "Strings are not supported by profile stats.");
|
||
|
}
|
||
|
|
||
|
statAssertf(RL_PROFILESTATS_TYPE_INVALID != typeIsCorrect, "Type '%d : %s' is not supported by profile stats", GetType(), GetTypeLabel());
|
||
|
|
||
|
return typeIsCorrect;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sStatData::ProfileStatMatches(const rlProfileStatsValue* val)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
if (m_Desc.GetIsProfileStat() && statVerify(val))
|
||
|
{
|
||
|
if (ExpectedProfileStatType(val))
|
||
|
{
|
||
|
if (GetIsBaseType(STAT_TYPE_INT) || GetIsBaseType(STAT_TYPE_TEXTLABEL) || GetIsBaseType(STAT_TYPE_PROFILE_SETTING))
|
||
|
{
|
||
|
result = (val->GetInt32() == GetInt());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_FLOAT))
|
||
|
{
|
||
|
result = (val->GetFloat() == GetFloat());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_BOOLEAN))
|
||
|
{
|
||
|
result = (!(0 == val->GetInt32()) == GetBoolean());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT8))
|
||
|
{
|
||
|
result = (static_cast<u8>(val->GetInt32()) == GetUInt8());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT16))
|
||
|
{
|
||
|
result = (static_cast<u16>(val->GetInt32()) == GetUInt16());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT32))
|
||
|
{
|
||
|
result = (static_cast<u32>(val->GetInt64()) == GetUInt32());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT64)
|
||
|
|| GetIsBaseType(STAT_TYPE_DATE)
|
||
|
|| GetIsBaseType(STAT_TYPE_POS)
|
||
|
|| GetIsBaseType(STAT_TYPE_PACKED)
|
||
|
|| GetIsBaseType(STAT_TYPE_USERID))
|
||
|
{
|
||
|
result = (static_cast<u64>(val->GetInt64()) == GetUInt64());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_INT64))
|
||
|
{
|
||
|
result = (val->GetInt64() == GetInt64());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statAssert(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sStatData::ReadInProfileStat(const rlProfileStatsValue* val, bool& flushStat)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
flushStat = false;
|
||
|
|
||
|
if (m_Desc.GetIsProfileStat() && statVerify(val))
|
||
|
{
|
||
|
statAssert(!m_Desc.GetIsCommunityStat());
|
||
|
|
||
|
if ( !m_Desc.GetSynched() && m_Desc.GetServerAuthoritative() && !m_Desc.GetIsCommunityStat() )
|
||
|
{
|
||
|
result = true;
|
||
|
|
||
|
statAssert(!m_Desc.GetDirty());
|
||
|
m_Desc.SetDirty(false);
|
||
|
|
||
|
if (ExpectedProfileStatType(val))
|
||
|
{
|
||
|
m_Desc.SetSynched(true);
|
||
|
|
||
|
if (GetIsBaseType(STAT_TYPE_INT) || GetIsBaseType(STAT_TYPE_TEXTLABEL))
|
||
|
{
|
||
|
SetInt(val->GetInt32());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_FLOAT))
|
||
|
{
|
||
|
SetFloat(val->GetFloat());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_BOOLEAN))
|
||
|
{
|
||
|
SetBoolean(!(0 == val->GetInt32()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT8))
|
||
|
{
|
||
|
SetUInt8(static_cast<u8>(val->GetInt32()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT16))
|
||
|
{
|
||
|
SetUInt16(static_cast<u16>(val->GetInt32()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT32))
|
||
|
{
|
||
|
SetUInt32(static_cast<u32>(val->GetInt64()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT64)
|
||
|
|| GetIsBaseType(STAT_TYPE_DATE)
|
||
|
|| GetIsBaseType(STAT_TYPE_POS)
|
||
|
|| GetIsBaseType(STAT_TYPE_PACKED)
|
||
|
|| GetIsBaseType(STAT_TYPE_USERID))
|
||
|
{
|
||
|
SetUInt64(static_cast<u64>(val->GetInt64()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_INT64))
|
||
|
{
|
||
|
SetInt64(val->GetInt64());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_Desc.SetSynched(false);
|
||
|
BANK_ONLY( statErrorf("Synch Failed for stat - Unknown Stat Type %s.", GetTypeLabel()); )
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BANK_ONLY(char buf[48];)
|
||
|
BANK_ONLY( statErrorf("Synch Failed for stat - expected type does not match profile server config: \"%s\"", val->ToString(buf, 48)); )
|
||
|
}
|
||
|
}
|
||
|
else if (m_Desc.GetSynched() && !m_Desc.GetIsCommunityStat())
|
||
|
{
|
||
|
//Set profile stat as Dirty, which means its current value needs to be written to the backend.
|
||
|
|
||
|
//We do this regardless of whether or not the stat is server authoritative. Once we've synched
|
||
|
//with the server, we want our value to be authoritative. This happens when, for example, we
|
||
|
//reset stats. We reset the value locally, and keep the stat marked as synced, and issue a
|
||
|
//reset on the server. If the reset on the server fails, we want to flush this stat again
|
||
|
//and keep the value on the client
|
||
|
|
||
|
if (m_Desc.GetBlockSynchEventFlush() && !m_Desc.GetDirtyInThisSession())
|
||
|
{
|
||
|
flushStat = false;
|
||
|
}
|
||
|
else if (!ProfileStatMatches(val))
|
||
|
{
|
||
|
flushStat = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
sStatData::WriteOutProfileStat(rlProfileStatsValue* val)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
if (statVerify(val) && m_Desc.GetIsProfileStat() && m_Desc.GetSynched())
|
||
|
{
|
||
|
statAssert( !StatsInterface::GetBlockSaveRequests() );
|
||
|
|
||
|
result = true;
|
||
|
|
||
|
if (GetIsBaseType(STAT_TYPE_INT) || GetIsBaseType(STAT_TYPE_TEXTLABEL) || GetIsBaseType(STAT_TYPE_PROFILE_SETTING))
|
||
|
{
|
||
|
val->SetInt32(GetInt());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_FLOAT))
|
||
|
{
|
||
|
val->SetFloat(GetFloat());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_BOOLEAN))
|
||
|
{
|
||
|
val->SetInt32(static_cast<int>(GetBoolean()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT8) || GetIsBaseType(STAT_TYPE_UINT16))
|
||
|
{
|
||
|
val->SetInt32(GetIsBaseType(STAT_TYPE_UINT8) ? static_cast<int>(GetUInt8()) : static_cast<int>(GetUInt16()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT32)
|
||
|
|| GetIsBaseType(STAT_TYPE_UINT64)
|
||
|
|| GetIsBaseType(STAT_TYPE_DATE)
|
||
|
|| GetIsBaseType(STAT_TYPE_POS)
|
||
|
|| GetIsBaseType(STAT_TYPE_PACKED)
|
||
|
|| GetIsBaseType(STAT_TYPE_USERID))
|
||
|
{
|
||
|
val->SetInt64(GetIsBaseType(STAT_TYPE_UINT32) ? static_cast<s64>(GetUInt32()) : static_cast<s64>(GetUInt64()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_INT64))
|
||
|
{
|
||
|
val->SetInt64(GetInt64());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = false;
|
||
|
BANK_ONLY( statErrorf("Failed to write profile stat - Invalid type (\"%s\")", GetTypeLabel()); )
|
||
|
}
|
||
|
|
||
|
#if __BLOCK_TAKEOVERS
|
||
|
//Territories stats are blocked from being sent to the server.
|
||
|
if (GetIsTerritoryStat() && !PARAM_enabletakeovers.Get())
|
||
|
{
|
||
|
if (val->GetInt32() != -1)
|
||
|
{
|
||
|
val->SetInt32(-1);
|
||
|
}
|
||
|
|
||
|
statAssert(-1 == val->GetInt32());
|
||
|
}
|
||
|
#endif // __BLOCK_TAKEOVERS
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool sStatData::ConstWriteOutProfileStat(rlProfileStatsValue* val) const
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
if (statVerify(val) && m_Desc.GetIsProfileStat())
|
||
|
{
|
||
|
result = true;
|
||
|
|
||
|
if (GetIsBaseType(STAT_TYPE_INT) || GetIsBaseType(STAT_TYPE_TEXTLABEL) || GetIsBaseType(STAT_TYPE_PROFILE_SETTING))
|
||
|
{
|
||
|
val->SetInt32(GetInt());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_FLOAT))
|
||
|
{
|
||
|
val->SetFloat(GetFloat());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_BOOLEAN))
|
||
|
{
|
||
|
val->SetInt32(static_cast<int>(GetBoolean()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT8) || GetIsBaseType(STAT_TYPE_UINT16))
|
||
|
{
|
||
|
val->SetInt32(GetIsBaseType(STAT_TYPE_UINT8) ? static_cast<int>(GetUInt8()) : static_cast<int>(GetUInt16()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT32)
|
||
|
|| GetIsBaseType(STAT_TYPE_UINT64)
|
||
|
|| GetIsBaseType(STAT_TYPE_DATE)
|
||
|
|| GetIsBaseType(STAT_TYPE_POS)
|
||
|
|| GetIsBaseType(STAT_TYPE_PACKED)
|
||
|
|| GetIsBaseType(STAT_TYPE_USERID))
|
||
|
{
|
||
|
val->SetInt64(GetIsBaseType(STAT_TYPE_UINT32) ? static_cast<s64>(GetUInt32()) : static_cast<s64>(GetUInt64()));
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_INT64))
|
||
|
{
|
||
|
val->SetInt64(GetInt64());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result = false;
|
||
|
BANK_ONLY( statErrorf("Failed to write profile stat - Invalid type (\"%s\")", GetTypeLabel()); )
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sStatData::SetDirty(const bool bDirtyProfile)
|
||
|
{
|
||
|
// Set profile stat as Dirty, which means its current value needs to be written to the backend.
|
||
|
if (m_Desc.GetIsProfileStat())
|
||
|
{
|
||
|
m_Desc.SetDirty(true, bDirtyProfile);
|
||
|
m_Desc.SetDirtyInThisSession(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sStatData::ResynchServerAuthoritative()
|
||
|
{
|
||
|
if (m_Desc.GetServerAuthoritative())
|
||
|
{
|
||
|
m_Desc.SetSynched(false);
|
||
|
m_Desc.SetDirty(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if __BANK
|
||
|
void
|
||
|
sStatData::SetDirtyBank()
|
||
|
{
|
||
|
// Set profile stat as Dirty, which means its current value needs to be written to the backend.
|
||
|
if (m_Desc.GetIsProfileStat() && NetworkInterface::GetActiveGamerInfo())
|
||
|
{
|
||
|
m_Desc.SetDirty(true);
|
||
|
m_Desc.SetDirtySavegame(true);
|
||
|
m_Desc.SetSynched(true);
|
||
|
m_Desc.SetForcedSynched(false);
|
||
|
}
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
|
||
|
#if !__FINAL
|
||
|
|
||
|
char*
|
||
|
sStatData::ValueToString(char* buf, const unsigned bufSize) const
|
||
|
{
|
||
|
if (buf && bufSize)
|
||
|
{
|
||
|
buf[0] = '\0';
|
||
|
|
||
|
if (GetIsBaseType(STAT_TYPE_TEXTLABEL))
|
||
|
{
|
||
|
if (TheText.DoesTextLabelExist(GetInt()))
|
||
|
safecpy( buf, TheText.Get(GetInt(), "STAT_LABEL"), bufSize );
|
||
|
else if (0 == GetInt())
|
||
|
formatf(buf, bufSize, "%s", "Null");
|
||
|
else
|
||
|
formatf(buf, bufSize, "Unknown string key=%d", GetInt());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_INT) || GetIsBaseType(STAT_TYPE_PROFILE_SETTING))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%d", GetInt());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_FLOAT))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%f", GetFloat());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_BOOLEAN))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%s", GetBoolean() ? "True": "False");
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT8))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%u", GetUInt8());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT16))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%u", GetUInt16());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT32))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%u", GetUInt32());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_UINT64) || GetIsBaseType(STAT_TYPE_DATE) ||GetIsBaseType(STAT_TYPE_POS) ||GetIsBaseType(STAT_TYPE_PACKED))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%" I64FMT "d", GetUInt64());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_INT64))
|
||
|
{
|
||
|
formatf(buf, bufSize, "%" I64FMT "d", GetInt64());
|
||
|
}
|
||
|
else if (GetIsBaseType(STAT_TYPE_USERID))
|
||
|
{
|
||
|
GetUserId(buf, bufSize);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
safecpy(buf, "Value Unknown", bufSize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sStatData::ToString() const
|
||
|
{
|
||
|
BANK_ONLY(char buff[48];)
|
||
|
BANK_ONLY( statDebugf3(" - type=\"%s\", value=\"%s\"", sStatsTypesNames[GetType()].m_Name, ValueToString(buff, 48)); )
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sStatData::AssertsAtCompileTime()
|
||
|
{
|
||
|
// If a new data type was added to enum eFrontendStatType then this struct has to know to what
|
||
|
// base type it belongs - int, char, float, etc.
|
||
|
// Set methods GetIsBaseType(STAT_TYPE_TEXTLABEL), GetIsBaseType(STAT_TYPE_INT), GetIsBaseType(STAT_TYPE_FLOAT)
|
||
|
CompileTimeAssert(NUMBER_OF_STATS_TYPE_IDS == MAX_STAT_TYPE);
|
||
|
}
|
||
|
|
||
|
#endif // !__FINAL
|
||
|
|
||
|
class CStatsDisplayListFileMounter : public CDataFileMountInterface
|
||
|
{
|
||
|
virtual bool LoadDataFile(const CDataFileMgr::DataFile & file)
|
||
|
{
|
||
|
switch(file.m_fileType)
|
||
|
{
|
||
|
case CDataFileMgr::SP_STATS_DISPLAY_LIST_FILE:
|
||
|
CStatsMgr::GetStatsDataMgr().LoadDataXMLFile(file.m_filename, true);
|
||
|
CStatsMgr::GetStatsDataMgr().PostLoad();
|
||
|
return true;
|
||
|
case CDataFileMgr::MP_STATS_DISPLAY_LIST_FILE:
|
||
|
CStatsMgr::GetStatsDataMgr().LoadDataXMLFile(file.m_filename, false);
|
||
|
CStatsMgr::GetStatsDataMgr().PostLoad();
|
||
|
return true;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual void UnloadDataFile(const CDataFileMgr::DataFile& file)
|
||
|
{
|
||
|
CStatsMgr::GetStatsDataMgr().ClearProcessFile(file.m_filename);
|
||
|
}
|
||
|
|
||
|
} g_StatsDisplayListDataFileMounter;
|
||
|
|
||
|
|
||
|
// --- CStatsDataMetricMgr ----------------------------------------------------------
|
||
|
|
||
|
class MetricUNLOCK_AGGREGATE : public MetricPlayerCoords
|
||
|
{
|
||
|
RL_DECLARE_METRIC(UCKAGG, TELEMETRY_CHANNEL_AMBIENT, LOGLEVEL_VERYHIGH_PRIORITY);
|
||
|
|
||
|
public:
|
||
|
explicit MetricUNLOCK_AGGREGATE (const CStatsDataMetricMgr::TrackedData& data)
|
||
|
: m_Data(data)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
virtual bool Write(RsonWriter* rw) const
|
||
|
{
|
||
|
if (!Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS", 0x756B4E30), false))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool result = MetricPlayStat::Write(rw);
|
||
|
|
||
|
result &= rw->WriteUns("a", m_Data.m_Count);
|
||
|
result &= rw->WriteUns("b", m_Data.m_NumStats);
|
||
|
result &= rw->WriteUns("c", m_Data.m_ElapsedTime);
|
||
|
|
||
|
if (m_Data.m_LevelUpElapsedTime > 0)
|
||
|
{
|
||
|
result &= rw->WriteUns("d", m_Data.m_LevelUpElapsedTime);
|
||
|
}
|
||
|
|
||
|
if (m_Data.m_Rank > -1)
|
||
|
{
|
||
|
result &= rw->WriteInt("e", m_Data.m_Rank);
|
||
|
}
|
||
|
|
||
|
if (m_Data.m_SessionType > -1)
|
||
|
{
|
||
|
result &= rw->WriteInt("f", m_Data.m_SessionType);
|
||
|
}
|
||
|
|
||
|
if (m_Data.m_MissionType > 0)
|
||
|
{
|
||
|
result &= rw->WriteUns("g", m_Data.m_MissionType);
|
||
|
}
|
||
|
|
||
|
if (m_Data.m_RootContentId > 0)
|
||
|
{
|
||
|
result &= rw->WriteUns("h", m_Data.m_RootContentId);
|
||
|
}
|
||
|
|
||
|
const u32 duration = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS_ELAPSED_TIME", 0x60E729EC), 0);
|
||
|
if (duration > 0)
|
||
|
{
|
||
|
result &= rw->WriteUns("i", duration);
|
||
|
}
|
||
|
const u32 minCount = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS_MIN_THRESHOLDCOUNT", 0x8005B74D), 0);
|
||
|
if (minCount > 0)
|
||
|
{
|
||
|
result &= rw->WriteUns("j", minCount);
|
||
|
}
|
||
|
const u32 minStatCount = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS_MIN_COUNT", 0x7AB6F82B), 500);
|
||
|
if (minStatCount > 0)
|
||
|
{
|
||
|
result &= rw->WriteUns("l", minStatCount);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
CStatsDataMetricMgr::TrackedData m_Data;
|
||
|
};
|
||
|
|
||
|
void CStatsDataMetricMgr::Update()
|
||
|
{
|
||
|
//We are offline - we cant send any metrics
|
||
|
if (!NetworkInterface::IsLocalPlayerOnline())
|
||
|
return;
|
||
|
|
||
|
//No stats registered for tracking
|
||
|
if (m_Stats.GetNumUsed() <= 0)
|
||
|
return;
|
||
|
|
||
|
//Network game is not in progress and no stats count.
|
||
|
//Otherwise we will let the logic run regardless and send the metric even if we are in SP.
|
||
|
if (!NetworkInterface::IsGameInProgress() && m_LastFlushTime == 0)
|
||
|
return;
|
||
|
|
||
|
//At least we need 500 in the number of times stats have changed.
|
||
|
const u32 minStatCount = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS_MIN_COUNT", 0x7AB6F82B), 500);
|
||
|
if (m_StatsUpdateCount < minStatCount)
|
||
|
return;
|
||
|
|
||
|
//We are starting up
|
||
|
if (m_LastFlushTime == 0)
|
||
|
{
|
||
|
m_LastFlushTime = sysTimer::GetSystemMsTime();;
|
||
|
}
|
||
|
|
||
|
const u32 currentTime = sysTimer::GetSystemMsTime();
|
||
|
const u32 elapsedTime = (currentTime - m_LastFlushTime);
|
||
|
|
||
|
//Minimum elapsed time
|
||
|
static const u32 MIN_ELAPSED_TIME = 1 * 60 * 1000;
|
||
|
const u32 minStatThresholdCount = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS_MIN_THRESHOLDCOUNT", 0x8005B74D), 0);
|
||
|
//Minimum number of stats threshold count reached.
|
||
|
const bool minNumStatsThresholdReached = (elapsedTime > MIN_ELAPSED_TIME && (0 == minStatThresholdCount || m_StatsUpdateCount >= minStatThresholdCount));
|
||
|
|
||
|
//Default elapsed time
|
||
|
static const u32 DEFAULT_ELAPSED_TIME = 5 * 60 * 1000;
|
||
|
const u32 elapsedTimeMin = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS_ELAPSED_TIME", 0x60E729EC), DEFAULT_ELAPSED_TIME);
|
||
|
if (elapsedTime > elapsedTimeMin || minNumStatsThresholdReached)
|
||
|
{
|
||
|
m_LastFlushTime = currentTime;
|
||
|
|
||
|
//Allowed to send the metric
|
||
|
if (Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ALLOW_STAT_METRICS", 0x756B4E30), false))
|
||
|
{
|
||
|
//Number of times metric has been flushed.
|
||
|
m_FlushCount += 1;
|
||
|
|
||
|
//total count
|
||
|
u32 numStats = 0;
|
||
|
u32 totalCount = 0;
|
||
|
StatsMetricsMap::Iterator iter = m_Stats.CreateIterator();
|
||
|
for (iter.Start(); !iter.AtEnd(); iter.Next())
|
||
|
{
|
||
|
u32& count = iter.GetData();
|
||
|
if (count > 0)
|
||
|
{
|
||
|
totalCount += count;
|
||
|
numStats += 1;
|
||
|
|
||
|
//Reset the stat count
|
||
|
count = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CStatsDataMetricMgr::TrackedData data;
|
||
|
data.m_Count = totalCount;
|
||
|
data.m_NumStats = numStats;
|
||
|
data.m_ElapsedTime = static_cast<u32>(elapsedTime / 1000);
|
||
|
data.m_LevelUpElapsedTime = (m_LevelUpElapsedTime > 0 && currentTime > m_LevelUpElapsedTime) ? static_cast<u32>((currentTime - m_LevelUpElapsedTime) / 1000) : 0;
|
||
|
data.m_Rank = StatsInterface::GetIntStat(STAT_RANK_FM);
|
||
|
data.m_SessionType = NetworkInterface::IsGameInProgress() ? NetworkInterface::GetSessionTypeForTelemetry() : -1;
|
||
|
data.m_MissionType = NetworkInterface::IsGameInProgress() ? CNetworkTelemetry::GetMatchStartedId() : 0;
|
||
|
data.m_RootContentId = NetworkInterface::IsGameInProgress() ? CNetworkTelemetry::GetRootContentId() : 0;
|
||
|
|
||
|
//Append and Flush out.
|
||
|
MetricUNLOCK_AGGREGATE m(data);
|
||
|
APPEND_METRIC_FLUSH(m, true);
|
||
|
}
|
||
|
|
||
|
//Clear last flush time because we are in SP.
|
||
|
if (!NetworkInterface::IsGameInProgress())
|
||
|
{
|
||
|
m_LastFlushTime = 0;
|
||
|
}
|
||
|
|
||
|
//Clear stats update count
|
||
|
m_StatsUpdateCount = 0;
|
||
|
//Clear Elapsed Level Up time
|
||
|
m_LevelUpElapsedTime = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CStatsDataMetricMgr::RegisterNewStatMetric(sStatParser& statparser, const StatId& key)
|
||
|
{
|
||
|
if (statparser.m_Desc.GetIsOnlineData() && !m_Stats.Access(key))
|
||
|
{
|
||
|
if (statparser.m_MetricSettingId == ATSTRINGHASH("TRACK_IN_DIG", 0x23A14FAD))
|
||
|
{
|
||
|
statDebugf1("Add new metric stat '%s'.", statparser.m_Name.c_str());
|
||
|
m_Stats.Insert(key, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CStatsDataMetricMgr::RegisterStatChanged(const StatId& statKey)
|
||
|
{
|
||
|
u32* count = m_Stats.Access(statKey);
|
||
|
if (count)
|
||
|
{
|
||
|
*count += 1;
|
||
|
m_StatsUpdateCount += 1;
|
||
|
}
|
||
|
|
||
|
if (NetworkInterface::IsGameInProgress())
|
||
|
{
|
||
|
//Level up
|
||
|
if (statKey == STAT_RANK_FM.GetStatId())
|
||
|
{
|
||
|
m_LevelUpElapsedTime = sysTimer::GetSystemMsTime();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// --- CStatsDataMgr ----------------------------------------------------------
|
||
|
|
||
|
CStatsDataMgr::~CStatsDataMgr()
|
||
|
{
|
||
|
Shutdown(SHUTDOWN_CORE);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::ClearProcessFile(const char* file)
|
||
|
{
|
||
|
if (statVerify(file))
|
||
|
{
|
||
|
statWarningf("ClearProcessFile - %s.", file);
|
||
|
atHashString filehash(file);
|
||
|
m_ProcessedFiles.Delete(filehash);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Init(unsigned initMode)
|
||
|
{
|
||
|
if(INIT_CORE == initMode)
|
||
|
{
|
||
|
#if __BANK
|
||
|
sStatData::ms_obfuscationDisabled = PARAM_disableStatObfuscation.Get();
|
||
|
#endif // __BANK
|
||
|
|
||
|
CDataFileMount::RegisterMountInterface(CDataFileMgr::SP_STATS_DISPLAY_LIST_FILE, &g_StatsDisplayListDataFileMounter, eDFMI_UnloadFirst);
|
||
|
CDataFileMount::RegisterMountInterface(CDataFileMgr::MP_STATS_DISPLAY_LIST_FILE, &g_StatsDisplayListDataFileMounter, eDFMI_UnloadFirst);
|
||
|
statAssert(m_aStatsData.GetCount() == 0);
|
||
|
LoadDataFiles();
|
||
|
PostLoad();
|
||
|
|
||
|
m_CommunityStats.Init();
|
||
|
}
|
||
|
else if(INIT_SESSION == initMode)
|
||
|
{
|
||
|
statWarningf("INIT_SESSION - GetGameSessionState=%d.", CGameSessionStateMachine::GetGameSessionState());
|
||
|
|
||
|
if (CGameSessionStateMachine::START_NEW_GAME == CGameSessionStateMachine::GetGameSessionState())
|
||
|
{
|
||
|
//We need to reload the stats xml files because we need to crc them again.
|
||
|
statWarningf("Reset Processed files - m_ProcessedFiles.");
|
||
|
m_ProcessedFiles.Reset();
|
||
|
|
||
|
#if __ASSERT
|
||
|
m_numObfuscatedStatTypes = 0;
|
||
|
#endif // __ASSERT
|
||
|
}
|
||
|
|
||
|
LoadDataFiles();
|
||
|
PostLoad();
|
||
|
BANK_ONLY(Bank_WidgetShutdown();)
|
||
|
BANK_ONLY(Bank_InitWidgets();)
|
||
|
|
||
|
//We are starting a Fresh New Game.
|
||
|
// - Reset Single Player stats and make a server profile stats reset.
|
||
|
if ((CGameSessionStateMachine::START_NEW_GAME == CGameSessionStateMachine::GetGameSessionState()) //Starting a New Game
|
||
|
|| (CGameSessionStateMachine::HANDLE_LOADED_SAVE_GAME == CGameSessionStateMachine::GetGameSessionState()) //Loading a savegame
|
||
|
|| (CGameSessionStateMachine::AUTOLOAD_SAVE_GAME == CGameSessionStateMachine::GetGameSessionState()) //Loading a savegame
|
||
|
)
|
||
|
{
|
||
|
statAssert(!NetworkInterface::IsGameInProgress());
|
||
|
//We don't need to dirty the profile here, because we reset the group below
|
||
|
ResetAllStats( true, false/*dirtyProfile*/ );
|
||
|
|
||
|
//Only do a Profile Stats Group Reset if this is a new game.
|
||
|
if (CGameSessionStateMachine::START_NEW_GAME == CGameSessionStateMachine::GetGameSessionState())
|
||
|
{
|
||
|
CProfileStats& profilestats = CLiveManager::GetProfileStatsMgr();
|
||
|
for(u32 i=PS_GRP_SP_START; i<=PS_GRP_SP_END; i++)
|
||
|
profilestats.ResetGroup(i);
|
||
|
}
|
||
|
|
||
|
//Reset our timestamps to 0 since we're on a brand new save
|
||
|
m_ProfileStatsTimestampLoadedFromSavegame = 0;
|
||
|
m_SavegameTimestampLoadedFromSavegame = 0;
|
||
|
}
|
||
|
|
||
|
//Update the values of all community stats.
|
||
|
UpdateCommunityStatValues();
|
||
|
}
|
||
|
|
||
|
m_SaveMgr.Init(initMode);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Shutdown(unsigned shutdownMode)
|
||
|
{
|
||
|
if(shutdownMode == SHUTDOWN_CORE)
|
||
|
{
|
||
|
Clear();
|
||
|
}
|
||
|
else if(shutdownMode == INIT_SESSION)
|
||
|
{
|
||
|
#if __BANK
|
||
|
Bank_WidgetShutdown();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
m_SaveMgr.Shutdown(shutdownMode);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Update()
|
||
|
{
|
||
|
//Saves manager update
|
||
|
m_SaveMgr.Update();
|
||
|
|
||
|
//Online bounded operations.
|
||
|
//No point doing anything if the local player is not Online.
|
||
|
if (!NetworkInterface::IsLocalPlayerOnline())
|
||
|
return;
|
||
|
|
||
|
// --------------------------------------------
|
||
|
// Network game in progress
|
||
|
// --------------------------------------------
|
||
|
if (NetworkInterface::IsGameInProgress())
|
||
|
{
|
||
|
m_ReadSessionProfileStats.Update();
|
||
|
}
|
||
|
|
||
|
//Update stats metrics
|
||
|
m_StatsMetricsMgr.Update();
|
||
|
|
||
|
#if __BANK
|
||
|
if (!m_SyncConsistencyStatus.Pending() && !m_SyncConsistencyStatus.None())
|
||
|
{
|
||
|
m_SyncConsistencyStatus.Reset();
|
||
|
profileStatDebugf("Finished profile sync consistency check. Look out for any errors above.");
|
||
|
}
|
||
|
UpdateDebugDraw();
|
||
|
#endif//#if __BANK
|
||
|
}
|
||
|
|
||
|
void CStatsDataMgr::LoadDataFiles()
|
||
|
{
|
||
|
strStreamingEngine::GetLoader().CallKeepAliveCallback();
|
||
|
|
||
|
const CDataFileMgr::DataFile* pData = DATAFILEMGR.GetLastFile(CDataFileMgr::SP_STATS_DISPLAY_LIST_FILE);
|
||
|
while(DATAFILEMGR.IsValid(pData))
|
||
|
{
|
||
|
if (!pData->m_disabled)
|
||
|
{
|
||
|
LoadDataXMLFile(pData->m_filename);
|
||
|
}
|
||
|
pData = DATAFILEMGR.GetPrevFile(pData);
|
||
|
strStreamingEngine::GetLoader().CallKeepAliveCallbackIfNecessary();
|
||
|
}
|
||
|
|
||
|
strStreamingEngine::GetLoader().CallKeepAliveCallback();
|
||
|
|
||
|
pData = DATAFILEMGR.GetLastFile(CDataFileMgr::MP_STATS_DISPLAY_LIST_FILE);
|
||
|
strStreamingEngine::GetLoader().CallKeepAliveCallbackIfNecessary();
|
||
|
while(DATAFILEMGR.IsValid(pData))
|
||
|
{
|
||
|
if (!pData->m_disabled)
|
||
|
{
|
||
|
LoadDataXMLFile(pData->m_filename,false);
|
||
|
}
|
||
|
pData = DATAFILEMGR.GetPrevFile(pData);
|
||
|
strStreamingEngine::GetLoader().CallKeepAliveCallbackIfNecessary();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CStatsDataMgr::PostLoad()
|
||
|
{
|
||
|
CStatsDataMgr::StatsMap::Iterator iter = StatIterBegin();
|
||
|
while (iter != StatIterEnd())
|
||
|
{
|
||
|
sStatDataPtr* ppStat = *iter;
|
||
|
if (!ppStat->KeyIsvalid())
|
||
|
{
|
||
|
sStatData* pStat = *ppStat;
|
||
|
if (pStat)
|
||
|
{
|
||
|
delete pStat;
|
||
|
}
|
||
|
|
||
|
m_aStatsData.Remove(iter);
|
||
|
}
|
||
|
|
||
|
++iter;
|
||
|
}
|
||
|
|
||
|
//Sort binary map.
|
||
|
m_aStatsData.FinishInsertion();
|
||
|
|
||
|
#if __ASSERT
|
||
|
StatId dup = m_aStatsData.FindDuplicates();
|
||
|
statAssertf(!dup.IsValid(), "Duplicate stat slot (id=\"%s (%d)\")", dup.GetName(), dup.GetHash());
|
||
|
|
||
|
// Sanity check the obfuscated types we're using
|
||
|
if(m_numObfuscatedStatTypes != EXPECTED_NUM_OBFUSCATED_STATS)
|
||
|
{
|
||
|
statWarningf("Mismatched EXPECTED_NUM_OBFUSCATED_STATS detected!");
|
||
|
statWarningf("The following types are obfuscated:");
|
||
|
statWarningf(" short = obfuscated int");
|
||
|
statWarningf(" double = obfuscated float");
|
||
|
statWarningf(" x64 = obfuscated u64");
|
||
|
statWarningf(" long = obfuscated s64");
|
||
|
statWarningf("List of obfuscated stats:");
|
||
|
for (StatsMap::Iterator iter = m_aStatsData.Begin(); iter != m_aStatsData.End(); ++iter)
|
||
|
{
|
||
|
const StatId& key = iter.GetKey();
|
||
|
if (!key.IsValid())
|
||
|
continue;
|
||
|
|
||
|
sStatData* pStat = *iter;
|
||
|
if(pStat->IsObfuscated())
|
||
|
{
|
||
|
atStatNameString hashStr = key;
|
||
|
statWarningf(" %s", hashStr.GetCStr());
|
||
|
}
|
||
|
}
|
||
|
statAssertf(false, "Got %d obfuscated stat types, but we expected %d! Either update "
|
||
|
"EXPECTED_NUM_OBFUSCATED_STATS, or change the type of the above stats.",
|
||
|
m_numObfuscatedStatTypes, EXPECTED_NUM_OBFUSCATED_STATS);
|
||
|
}
|
||
|
#endif //__ASSERT
|
||
|
|
||
|
DEV_ONLY( ValidateStaticStatsIds(); )
|
||
|
BANK_ONLY( Bank_CalculateAllDataSizes(); )
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::ResetStat(sStatDataPtr* ppStat, const bool dirtyProfile)
|
||
|
{
|
||
|
if (statVerify(ppStat) && statVerify(ppStat->GetKey().IsValid()))
|
||
|
{
|
||
|
sStatData* pStat = *ppStat;
|
||
|
if (statVerify(pStat))
|
||
|
{
|
||
|
BANK_ONLY( statDebugf2("Reset stat %s value to 0", ppStat->GetKeyName()); )
|
||
|
|
||
|
const bool valueWillchange = !pStat->IsDefaultValue();
|
||
|
|
||
|
pStat->Reset();
|
||
|
|
||
|
if (pStat->GetDesc().GetIsProfileStat() && !GetSavesMgr().GetBlockSaveRequests())
|
||
|
{
|
||
|
if (valueWillchange)
|
||
|
{
|
||
|
if (dirtyProfile)
|
||
|
{
|
||
|
pStat->GetDesc().SetDirty(true);
|
||
|
}
|
||
|
pStat->GetDesc().SetDirtySavegame(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////
|
||
|
// GTA V Exceptions for stats that are not Reset to 0
|
||
|
static StatId s_NatType = HASH_STAT_ID("_NatType");
|
||
|
static StatId s_AcctSubscriptionLevel = HASH_STAT_ID("_AcctSubscriptionLevel");
|
||
|
static StatId s_reportStrength = ATSTRINGHASH("MPPLY_REPORT_STRENGTH", 0x850CF917);
|
||
|
static StatId s_commendStrength = ATSTRINGHASH("MPPLY_COMMEND_STRENGTH", 0xADA7E7D3);
|
||
|
if (ppStat->GetKey() == s_reportStrength || ppStat->GetKey() == s_commendStrength)
|
||
|
{
|
||
|
int data = 16; bool retHaschangedValue = false;
|
||
|
pStat->SetData(&data, sizeof(int), retHaschangedValue);
|
||
|
}
|
||
|
else if (ppStat->GetKey() == s_NatType || ppStat->GetKey() == s_AcctSubscriptionLevel)
|
||
|
{
|
||
|
int data = -1; bool retHaschangedValue = false;
|
||
|
pStat->SetData(&data, sizeof(int), retHaschangedValue);
|
||
|
}
|
||
|
|
||
|
#if __BANK
|
||
|
if(0 < pStat->GetDesc().GetFlushPriority())
|
||
|
{
|
||
|
OUTPUT_ONLY( char buff[48]; )
|
||
|
statDebugf3("%s - Reset stat name='%s'(hash='%d',group='%d',category='%d') value to '%s'.",CTheScripts::GetCurrentScriptNameAndProgramCounter()
|
||
|
,ppStat->GetKeyName()
|
||
|
,(int)ppStat->GetKey().GetHash()
|
||
|
,pStat->GetDesc().GetGroup()
|
||
|
,pStat->GetDesc().GetCategory()
|
||
|
,pStat->ValueToString(buff, 48));
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::ResetAllStats(const bool offlineDataOnly, const bool dirtyProfile)
|
||
|
{
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
for (;iter != m_aStatsData.End(); ++iter)
|
||
|
{
|
||
|
const StatId& key = iter.GetKey();
|
||
|
|
||
|
if (!key.IsValid())
|
||
|
continue;
|
||
|
|
||
|
sStatData* pStat = *iter;
|
||
|
|
||
|
//Reset all data or only the single player stats.
|
||
|
if (pStat && (!offlineDataOnly || !pStat->GetDesc().GetIsOnlineData()))
|
||
|
{
|
||
|
const sStatDescription desc = pStat->GetDesc();
|
||
|
if (desc.GetBlockReset())
|
||
|
{
|
||
|
statWarningf("Stat with key='%s' is Blocked to Reset.", iter.GetKeyName());
|
||
|
|
||
|
if (desc.GetSynched() && desc.GetDirty())
|
||
|
{
|
||
|
statWarningf("[ResetAllStats] Stat with key='%s' - Clearing Dirty FLAG.", iter.GetKeyName());
|
||
|
pStat->GetDesc().SetDirty( false );
|
||
|
}
|
||
|
|
||
|
//if (desc.GetSynched() && desc.GetSavegameDirty())
|
||
|
// pStat->GetDesc().SetDirtySavegame(false);
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ResetStat(*iter, dirtyProfile);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SignedOut()
|
||
|
{
|
||
|
#if __XENON
|
||
|
//We need to reload the stats xml files because we need to crc them again.
|
||
|
statWarningf("Reset Processed files - m_ProcessedFiles.");
|
||
|
m_ProcessedFiles.Reset();
|
||
|
#endif // __XENON
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
for (;iter != m_aStatsData.End();++iter)
|
||
|
{
|
||
|
const StatId& key = iter.GetKey();
|
||
|
|
||
|
if (!key.IsValid())
|
||
|
continue;
|
||
|
|
||
|
sStatData* pStat = *iter;
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
pStat->Reset();
|
||
|
pStat->m_Desc.SetSynched(false);
|
||
|
pStat->m_Desc.SetDirty(false);
|
||
|
pStat->m_Desc.SetDirtySavegame(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EndNetworkMatch();
|
||
|
m_SaveMgr.Shutdown(SHUTDOWN_CORE);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SignedOffline()
|
||
|
{
|
||
|
//Clear all Online DATA
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
for (;iter != m_aStatsData.End();++iter)
|
||
|
{
|
||
|
const StatId& key = iter.GetKey();
|
||
|
|
||
|
if (!key.IsValid())
|
||
|
continue;
|
||
|
|
||
|
sStatData* pStat = *iter;
|
||
|
|
||
|
if (pStat && pStat->GetDesc().GetIsOnlineData())
|
||
|
{
|
||
|
pStat->Reset();
|
||
|
pStat->m_Desc.SetSynched(false);
|
||
|
pStat->m_Desc.SetDirty(false);
|
||
|
pStat->m_Desc.SetDirtySavegame(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_SaveMgr.SignedOffline( );
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SignedIn()
|
||
|
{
|
||
|
m_SaveMgr.Init(INIT_SESSION);
|
||
|
}
|
||
|
|
||
|
#if !__NO_OUTPUT
|
||
|
void
|
||
|
CStatsDataMgr::SpewDirtyCloudOnlySats(const bool multiplayer)
|
||
|
{
|
||
|
if (PARAM_spewDirtySavegameStats.Get() && ((multiplayer && s_DirtySavegameMPStatsCount > 0) || (!multiplayer && s_DirtySavegameSPStatsCount > 0)))
|
||
|
{
|
||
|
statDebugf3("-----------------------------------------------------------------");
|
||
|
statDebugf3("Number of Dirty Stats: < %d >", multiplayer ? s_DirtySavegameMPStatsCount : s_DirtySavegameSPStatsCount);
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
for ( ; iter != m_aStatsData.End(); ++iter)
|
||
|
{
|
||
|
const StatId& key = iter.GetKey();
|
||
|
if (!key.IsValid())
|
||
|
continue;
|
||
|
|
||
|
sStatData* pStat = *iter;
|
||
|
if (pStat && pStat->GetDesc().GetSavegameDirty() && (pStat->GetDesc().GetIsOnlineData() == multiplayer))
|
||
|
{
|
||
|
statDebugf3(" ..... stat = < %s > ", iter.GetKey().GetName());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
statDebugf3("-----------------------------------------------------------------");
|
||
|
}
|
||
|
}
|
||
|
#endif // !__NO_OUTPUT
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::PreSaveBaseStats(const bool multiplayer)
|
||
|
{
|
||
|
//Reset some stats when a game is saved
|
||
|
StatId stat = StatsInterface::GetStatsModelHashId("KILLS_SINCE_LAST_CHECKPOINT");
|
||
|
if (StatsInterface::IsKeyValid(stat))
|
||
|
{
|
||
|
CStatsMgr::IncrementStat(StatsInterface::GetStatsModelHashId("TOTAL_LEGITIMATE_KILLS"), (float)StatsInterface::GetIntStat(stat));
|
||
|
StatsInterface::SetStatData(stat, 0);
|
||
|
}
|
||
|
|
||
|
//Setup timestamp of last save done by the single player game.
|
||
|
if (!multiplayer)
|
||
|
{
|
||
|
StatsInterface::SetStatData(STAT_SP_SAVE_TIMESTAMP, rlGetPosixTime(), STATUPDATEFLAG_DIRTY_PROFILE);
|
||
|
|
||
|
//Trigger a Force flush.
|
||
|
if (NetworkInterface::IsLocalPlayerOnline() && rlProfileStats::IsInitialized())
|
||
|
{
|
||
|
CLiveManager::GetProfileStatsMgr().Flush(true, false);
|
||
|
}
|
||
|
|
||
|
//Cache our timestamps that we loaded from this savegame
|
||
|
m_ProfileStatsTimestampLoadedFromSavegame = StatsInterface::GetUInt64Stat(STAT_SP_PROFILE_STAT_VERSION);
|
||
|
m_SavegameTimestampLoadedFromSavegame = StatsInterface::GetUInt64Stat(STAT_SP_SAVE_TIMESTAMP);
|
||
|
}
|
||
|
|
||
|
if ((multiplayer && s_DirtySavegameMPStatsCount > 0) || (!multiplayer && s_DirtySavegameSPStatsCount > 0))
|
||
|
{
|
||
|
statDebugf3("-----------------------------------------------------------------");
|
||
|
statDebugf3("Number of Dirty Stats: < %d >", multiplayer ? s_DirtySavegameMPStatsCount : s_DirtySavegameSPStatsCount);
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
for ( ; iter != m_aStatsData.End(); ++iter)
|
||
|
{
|
||
|
const StatId& key = iter.GetKey();
|
||
|
if (!key.IsValid())
|
||
|
continue;
|
||
|
|
||
|
sStatData* pStat = *iter;
|
||
|
if (pStat && pStat->GetDesc().GetSavegameDirty() && (pStat->GetDesc().GetIsOnlineData() == multiplayer))
|
||
|
{
|
||
|
statDebugf3(" ..... stat = < %s > ", iter.GetKey().GetName());
|
||
|
sStatData* stat = *iter;
|
||
|
stat->GetDesc().SetDirtySavegame(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
statDebugf3("-----------------------------------------------------------------");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::PostLoadBaseStats(const bool multiplayer)
|
||
|
{
|
||
|
//If this is the SP save, cache the profile stats timestamp that we loaded
|
||
|
//this savegame from
|
||
|
if (!multiplayer)
|
||
|
{
|
||
|
m_ProfileStatsTimestampLoadedFromSavegame = StatsInterface::GetUInt64Stat(STAT_SP_PROFILE_STAT_VERSION);
|
||
|
m_SavegameTimestampLoadedFromSavegame = StatsInterface::GetUInt64Stat(STAT_SP_SAVE_TIMESTAMP);
|
||
|
}
|
||
|
|
||
|
//Apply community stats data.
|
||
|
UpdateCommunityStatValues();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Clear()
|
||
|
{
|
||
|
m_OnlineDataIsSynched = false;
|
||
|
|
||
|
while(m_aStatsData.GetCount() > 0)
|
||
|
{
|
||
|
sStatDataPtr* ppStat = *m_aStatsData.Begin();
|
||
|
sStatData* pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
delete pStat;
|
||
|
}
|
||
|
|
||
|
statDebugf2("Destroying stat (id=%s)", ppStat->GetKeyName());
|
||
|
|
||
|
m_aStatsData.Remove(0);
|
||
|
}
|
||
|
|
||
|
statAssert(m_aStatsData.GetCount() == 0);
|
||
|
|
||
|
statAssert(0 == m_NumHighPriorityStats);
|
||
|
statAssert(!m_HighPriorityStatIds);
|
||
|
statAssert(0 == m_NumLowPriorityStats);
|
||
|
statAssert(!m_LowPriorityStatIds);
|
||
|
for (int i=0; i<MAX_NUM_REMOTE_GAMERS; i++)
|
||
|
{
|
||
|
statAssert(!m_HighPriorityRecords[i].GetValues());
|
||
|
statAssert(!m_LowPriorityRecords[i].GetValues());
|
||
|
}
|
||
|
|
||
|
//Just in case check this has been done
|
||
|
EndNetworkMatch();
|
||
|
|
||
|
#if __BANK
|
||
|
Bank_WidgetShutdown();
|
||
|
#endif
|
||
|
|
||
|
m_ProcessedFiles.Reset();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::ResetAllOnlineCharacterStats(const int prefix, const bool UNUSED_PARAM(saveGame), const bool localOnly)
|
||
|
{
|
||
|
if (statVerify(-1 < prefix))
|
||
|
{
|
||
|
//Magic number see eProfileStatsGroups FUCK THIS SHYTE - THIS CREATED LOADS OF FUCKING ISSUES.
|
||
|
const eProfileStatsGroups group = (eProfileStatsGroups)(prefix+1);
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
for ( ; iter != m_aStatsData.End(); ++iter)
|
||
|
{
|
||
|
const StatId& key = iter.GetKey();
|
||
|
|
||
|
if (!key.IsValid())
|
||
|
continue;
|
||
|
|
||
|
sStatData* pStat = *iter;
|
||
|
if (pStat)
|
||
|
{
|
||
|
const sStatDescription desc = pStat->GetDesc();
|
||
|
if (!desc.GetIsOnlineData())
|
||
|
continue;
|
||
|
|
||
|
if (desc.GetBlockReset())
|
||
|
{
|
||
|
statWarningf("Stat with key='%s' is Blocked to Reset.", iter.GetKeyName());
|
||
|
|
||
|
if (desc.GetSynched() && desc.GetDirty())
|
||
|
{
|
||
|
statWarningf("[ResetAllOnlineCharacterStats] Stat with key='%s' - Clearing Dirty FLAG.", iter.GetKeyName());
|
||
|
pStat->GetDesc().SetDirty( false );
|
||
|
}
|
||
|
|
||
|
//if (desc.GetSynched() && desc.GetSavegameDirty())
|
||
|
// pStat->GetDesc().SetDirtySavegame(false);
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
else if (desc.GetGroup() == group)
|
||
|
{
|
||
|
if (!desc.GetSynched()) statErrorf("Stat %s is not synched reset wil be forced.", iter.GetKeyName());
|
||
|
//We don't need to dirty the profile here, because we do a reset group below
|
||
|
ResetStat(*iter, false/*dirtyProfile*/);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!localOnly)
|
||
|
CLiveManager::GetProfileStatsMgr().ResetGroup( group );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const sStatData*
|
||
|
CStatsDataMgr::GetStat(const StatId& keyHash) const
|
||
|
{
|
||
|
if (statVerify(IsStatsDataValid()))
|
||
|
{
|
||
|
const sStatDataPtr* ppStat = m_aStatsData.UnSafeGet(keyHash);
|
||
|
if (ppStat && ppStat->KeyIsvalid())
|
||
|
{
|
||
|
return *ppStat;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
sStatData*
|
||
|
CStatsDataMgr::GetStat(const StatId& keyHash)
|
||
|
{
|
||
|
if (statVerify(IsStatsDataValid()))
|
||
|
{
|
||
|
sStatDataPtr* ppStat = m_aStatsData.UnSafeGet(keyHash);
|
||
|
if (ppStat && ppStat->KeyIsvalid())
|
||
|
{
|
||
|
return *ppStat;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const sStatDataPtr*
|
||
|
CStatsDataMgr::GetStatPtr(const StatId& keyHash) const
|
||
|
{
|
||
|
if (statVerify(IsStatsDataValid()))
|
||
|
{
|
||
|
return m_aStatsData.SafeGet(keyHash);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
sStatDataPtr*
|
||
|
CStatsDataMgr::GetStatPtr(const StatId& keyHash)
|
||
|
{
|
||
|
if (statVerify(IsStatsDataValid()))
|
||
|
{
|
||
|
return m_aStatsData.SafeGet(keyHash);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::IsKeyValid(const StatId& keyHash) const
|
||
|
{
|
||
|
if (IsStatsDataValid())
|
||
|
{
|
||
|
const sStatDataPtr* ppStat = m_aStatsData.SafeGet(keyHash);
|
||
|
return (ppStat && ppStat->KeyIsvalid());
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#if __BANK
|
||
|
void
|
||
|
CStatsDataMgr::SetStatDirty(const StatId& keyHash)
|
||
|
{
|
||
|
sStatData* pStat = GetStat(keyHash);
|
||
|
if (pStat && pStat->GetDesc().GetIsProfileStat())
|
||
|
{
|
||
|
pStat->SetDirtyBank();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CStatsDataMgr::GetStatObfuscationDisabled()
|
||
|
{
|
||
|
return PARAM_disableStatObfuscation.Get();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SetBlockProfileStatFlush(const StatId& keyHash, const bool value)
|
||
|
{
|
||
|
sStatData* stat = GetStat( keyHash );
|
||
|
if (gnetVerify( stat ))
|
||
|
{
|
||
|
sStatDescription& desc = stat->GetDesc();
|
||
|
desc.SetBlockProfileStatFlush( value );
|
||
|
|
||
|
if (desc.GetBlockSynchEventFlush())
|
||
|
{
|
||
|
if (!value)
|
||
|
{
|
||
|
desc.SetSynched(true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
desc.SetSynched(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::ResynchServerAuthoritative(const bool onlinestat)
|
||
|
{
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
|
||
|
if (iter.GetKey().IsValid())
|
||
|
pStat = *iter;
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
const sStatDescription& desc = pStat->GetDesc();
|
||
|
if (desc.GetIsOnlineData() == onlinestat && desc.GetServerAuthoritative())
|
||
|
{
|
||
|
ResetStat(*iter, false);
|
||
|
pStat->ResynchServerAuthoritative();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SetAllStatsToSynched(const int savegameSlot, const bool onlinestat, const bool includeServerAuthoritative)
|
||
|
{
|
||
|
statDebugf1(" ***** SET ALL STATS TO SYNCHED ***** ");
|
||
|
statDebugf1(" ........................... Slot = '%d'", savegameSlot);
|
||
|
statDebugf1(" ......................... Online = '%s'", onlinestat?"true":"false");
|
||
|
statDebugf1(" ........... Server Authoritative = '%s'", includeServerAuthoritative?"true":"false");
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat
|
||
|
&& !pStat->GetDesc().GetSynched()
|
||
|
&& (pStat->GetDesc().GetIsOnlineData() == onlinestat)
|
||
|
&& (includeServerAuthoritative || !pStat->GetDesc().GetServerAuthoritative())
|
||
|
&& (-1 == savegameSlot || savegameSlot == (int)pStat->GetDesc().GetCategory()))
|
||
|
{
|
||
|
pStat->GetDesc().SetSynched(true);
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SetAllStatsToSynchedByGroup(const eProfileStatsGroups group, const bool onlinestat, const bool includeServerAuthoritative)
|
||
|
{
|
||
|
statDebugf1(" ***** SET ALL STATS TO SYNCHED ***** ");
|
||
|
statDebugf1(" ........................... Slot = '%d'", group);
|
||
|
statDebugf1(" ......................... Online = '%s'", onlinestat?"true":"false");
|
||
|
statDebugf1(" ........... Server Authoritative = '%s'", includeServerAuthoritative?"true":"false");
|
||
|
statDebugf1(" ************************************ ");
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
sStatDescription& desc = pStat->GetDesc();
|
||
|
if (!desc.GetSynched() && desc.GetIsOnlineData() == onlinestat && (includeServerAuthoritative || !desc.GetServerAuthoritative()))
|
||
|
{
|
||
|
if (group == desc.GetGroup())
|
||
|
{
|
||
|
statDebugf1(" # Reset Synch Stat = '%s'", iter.GetKeyName());
|
||
|
pStat->GetDesc().SetSynched(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::GetAllOnlineStatsAreSynched( )
|
||
|
{
|
||
|
statDebugf1(" ***** GET ALL STATS TO SYNCHED ***** ");
|
||
|
|
||
|
bool result = true;
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat && !pStat->GetDesc().GetSynched() && pStat->GetDesc().GetIsOnlineData() && pStat->GetDesc().GetServerAuthoritative())
|
||
|
{
|
||
|
statErrorf("Stat %s is not synched after loading.", iter.GetKeyName());
|
||
|
result = false;
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SetStatSynched(const StatId& keyHash)
|
||
|
{
|
||
|
sStatData* pStat = GetStat(keyHash);
|
||
|
if (pStat)
|
||
|
{
|
||
|
pStat->GetDesc().SetSynched(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if __NET_SHOP_ACTIVE
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::SetIsPlayerInventory(StatsMap::Iterator& statIter)
|
||
|
{
|
||
|
if (statIter.IsValid())
|
||
|
{
|
||
|
StatId& statKey = statIter.GetKey();
|
||
|
DEV_ONLY(if (!statKey.IsValid()) statWarningf("Stat key=\"%s\" hash=\"%d\" is NOT VALID", statIter.GetKeyName(), statKey.GetHash());)
|
||
|
|
||
|
sStatData* pStat = 0;
|
||
|
if (statKey.IsValid())
|
||
|
{
|
||
|
pStat = *statIter;
|
||
|
}
|
||
|
|
||
|
if (statVerify(pStat))
|
||
|
{
|
||
|
if (pStat->GetDesc().GetPlayerInventory())
|
||
|
return true;
|
||
|
|
||
|
if (pStat->GetDesc().SetIsPlayerInventory())
|
||
|
{
|
||
|
pStat->GetDesc().SetSynched(true);
|
||
|
pStat->GetDesc().SetForcedSynched(false);
|
||
|
pStat->GetDesc().SetDirtyInThisSession(false);
|
||
|
|
||
|
statDebugf1("Stat key=\"%s\" hash=\"%d\" - SETUP AS PLAYER INVENTORY.", statIter.GetKeyName(), statKey.GetHash());
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statErrorf("Stat key=\"%s\" hash=\"%d\" - FAILED SETUP AS PLAYER INVENTORY.", statIter.GetKeyName(), statKey.GetHash());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::ClearPlayerInventory( )
|
||
|
{
|
||
|
GameTransactionSessionMgr& sessionMgr = GameTransactionSessionMgr::Get();
|
||
|
|
||
|
if (sessionMgr.IsSlotInvalid())
|
||
|
{
|
||
|
gnetDebug1("Avoid CStatsDataMgr::ClearPlayerInventory - GAME_SERVER slot is invalid.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const u32 slot = (sessionMgr.GetCharacterSlot()-CHAR_MP_START);
|
||
|
gnetDebug1("CStatsDataMgr::ClearPlayerInventory - GAME_SERVER slot is '%d'.", slot);
|
||
|
|
||
|
netCatalog& catalogInst = netCatalog::GetInstance();
|
||
|
const atMap < int, sPackedStatsScriptExceptions >& packedExc = catalogInst.GetScriptExceptions();
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
|
||
|
if (iter.GetKey().IsValid())
|
||
|
pStat = *iter;
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
sStatDescription& desc = pStat->GetDesc();
|
||
|
|
||
|
//Stats that are used by the player inventory - Either clear all or only clear the current selected character.
|
||
|
if (desc.GetPlayerInventory() && (desc.GetIsPlayerStat() || slot == desc.GetMultiplayerCharacterSlot()))
|
||
|
{
|
||
|
const sPackedStatsScriptExceptions* exc = packedExc.Access(iter.GetKey().GetHash());
|
||
|
|
||
|
//Based on certain exceptions we might have to reset it differently to make sure the
|
||
|
// bits used by script are not reset.
|
||
|
if (exc)
|
||
|
{
|
||
|
statAssert(pStat->GetIsBaseType(STAT_TYPE_PACKED));
|
||
|
|
||
|
//Cache current value.
|
||
|
const u64 value = pStat->GetUInt64();
|
||
|
pStat->Reset();
|
||
|
|
||
|
//Only needed if values are > 0
|
||
|
if (value > 0)
|
||
|
{
|
||
|
exc->SetExceptions(iter.GetKey(), value);
|
||
|
statDebugf1("Clear player Inventory Stat key=\"%s\" hash=\"%d\", before=\"%" I64FMT "d\", after=\"%" I64FMT "d\".", iter.GetKeyName(), iter.GetKey().GetHash(), value, pStat->GetUInt64());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statDebugf3("Clear player Inventory Stat key=\"%s\" hash=\"%d\".", iter.GetKeyName(), iter.GetKey().GetHash());
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pStat->Reset();
|
||
|
statDebugf3("Clear player Inventory Stat key=\"%s\" hash=\"%d\".", iter.GetKeyName(), iter.GetKey().GetHash());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
#endif //__NET_SHOP_ACTIVE
|
||
|
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SetStatSynched(StatsMap::Iterator& statIter)
|
||
|
{
|
||
|
StatId& statKey = statIter.GetKey();
|
||
|
DEV_ONLY(if (!statKey.IsValid()) statWarningf("Stat key=\"%s\" hash=\"%d\" is NOT VALID", statIter.GetKeyName(), statKey.GetHash());)
|
||
|
|
||
|
sStatData* pStat = 0;
|
||
|
if (statKey.IsValid())
|
||
|
{
|
||
|
pStat = *statIter;
|
||
|
}
|
||
|
|
||
|
if (statVerify(pStat))
|
||
|
{
|
||
|
pStat->GetDesc().SetSynched(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::GetStatIsNotNull(const StatId& keyHash) const
|
||
|
{
|
||
|
const sStatData* stat = GetStat(keyHash);
|
||
|
|
||
|
if (stat)
|
||
|
{
|
||
|
if (stat->GetIsBaseType(STAT_TYPE_INT) || stat->GetIsBaseType(STAT_TYPE_PROFILE_SETTING))
|
||
|
{
|
||
|
return (0 < stat->GetInt());
|
||
|
}
|
||
|
else if (stat->GetIsBaseType(STAT_TYPE_FLOAT))
|
||
|
{
|
||
|
return (0 < stat->GetFloat());
|
||
|
}
|
||
|
else if (stat->GetIsBaseType(STAT_TYPE_TEXTLABEL))
|
||
|
{
|
||
|
return (0 != stat->GetInt());
|
||
|
}
|
||
|
else if (stat->GetIsBaseType(STAT_TYPE_UINT8))
|
||
|
{
|
||
|
return (0 < stat->GetUInt8());
|
||
|
}
|
||
|
else if (stat->GetIsBaseType(STAT_TYPE_UINT16))
|
||
|
{
|
||
|
return (0 < stat->GetUInt16());
|
||
|
}
|
||
|
else if (stat->GetIsBaseType(STAT_TYPE_UINT32))
|
||
|
{
|
||
|
return (0 < stat->GetUInt32());
|
||
|
}
|
||
|
else if (stat->GetIsBaseType(STAT_TYPE_UINT64)
|
||
|
|| stat->GetIsBaseType(STAT_TYPE_DATE)
|
||
|
|| stat->GetIsBaseType(STAT_TYPE_POS)
|
||
|
|| stat->GetIsBaseType(STAT_TYPE_PACKED)
|
||
|
|| stat->GetIsBaseType(STAT_TYPE_USERID)
|
||
|
)
|
||
|
{
|
||
|
return (0 < stat->GetUInt64());
|
||
|
}
|
||
|
else if (stat->GetIsBaseType(STAT_TYPE_INT64))
|
||
|
{
|
||
|
return (0 < stat->GetInt64());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const char*
|
||
|
CStatsDataMgr::GetStatTypeLabel(const StatId& keyHash) const
|
||
|
{
|
||
|
const sStatData* pStat = GetStat(keyHash);
|
||
|
if (statVerify(pStat))
|
||
|
{
|
||
|
return pStat->GetTypeLabel();
|
||
|
}
|
||
|
return "NONE";
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::SetStatIterData(StatsMap::Iterator& statIter, void* pData, const unsigned sizeofData, const u32 flags)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
if (!CStatsUtils::IsStatsTrackingEnabled() && !(flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME) )
|
||
|
{
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
StatId& statKey = statIter.GetKey();
|
||
|
DEV_ONLY(if (!statKey.IsValid()) statWarningf("Stat key=\"%s\" hash=\"%d\" is NOT VALID", statIter.GetKeyName(), statKey.GetHash());)
|
||
|
|
||
|
sStatData* pStat = 0;
|
||
|
if (statKey.IsValid())
|
||
|
{
|
||
|
pStat = *statIter;
|
||
|
}
|
||
|
|
||
|
if (statVerify(pStat))
|
||
|
{
|
||
|
sStatDescription& statDesc = pStat->GetDesc();
|
||
|
|
||
|
//DO NOT Allow updates from level design to player inventory
|
||
|
if (flags&STATUPDATEFLAG_SCRIPT_UPDATE && statDesc.GetPlayerInventory())
|
||
|
{
|
||
|
statDebugf3("FAILED to Change Inventory Stat with key=\"%s\" hash=\"%d\".", statIter.GetKeyName(), statKey.GetHash());
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (flags&STATUPDATEFLAG_GAMER_SERVER && statDesc.GetPlayerInventory())
|
||
|
{
|
||
|
statWarningf("Change Inventory Stat with key=\"%s\" hash=\"%d\".", statIter.GetKeyName(), statKey.GetHash());
|
||
|
}
|
||
|
|
||
|
//Not a character stat (Model dependent) or the local player has a valid model.
|
||
|
if (flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME || !statDesc.GetIsCharacterStat() || statVerify(StatsInterface::GetStatsPlayerModelValid()))
|
||
|
{
|
||
|
if (pStat->GetIsBaseType(STAT_TYPE_STRING))
|
||
|
{
|
||
|
statAssertf(sizeofData <= pStat->GetSizeOfData(), "Failed Change STRING stat=\"%s\",type=\"%s\",size=\"%d\". Data size=\"%d\" is invalid.", statIter.GetKeyName(), pStat->GetTypeLabel(), pStat->GetSizeOfData(), sizeofData);
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_USERID))
|
||
|
{
|
||
|
// Do nothing
|
||
|
statAssertf(sizeofData <= pStat->GetSizeOfData(), "Failed Change USER_ID stat=\"%s\",type=\"%s\",size=\"%d\". Data size=\"%d\" is invalid.", statIter.GetKeyName(), pStat->GetTypeLabel(), pStat->GetSizeOfData(), sizeofData);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pStat->GetIsBaseType(STAT_TYPE_TEXTLABEL))
|
||
|
{
|
||
|
statAssertf(0 == *((int*)pData) || TheText.DoesTextLabelExist(*((int*)pData)), "Invalid string label=\"%d\" set for stat=\"%s\"", *((int*)pData), statIter.GetKeyName());
|
||
|
}
|
||
|
|
||
|
statAssertf(sizeofData == pStat->GetSizeOfData(), "Failed Change stat=\"%s\",type=\"%s\",size=\"%d\". Data size=\"%d\" is invalid.", statIter.GetKeyName(), pStat->GetTypeLabel(), pStat->GetSizeOfData(), sizeofData);
|
||
|
}
|
||
|
|
||
|
//Don't load server-authoritative stats from the MP savegame. We want to keep whatever we read from profile stats
|
||
|
if (flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME && statDesc.GetServerAuthoritative())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//Don't load MP stats from the SP savegame
|
||
|
if (!statVerify(!(flags&STATUPDATEFLAG_LOAD_FROM_SP_SAVEGAME && statDesc.GetIsOnlineData())))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Debug metric to help track down XP loss - B*2239619
|
||
|
int oldValue=0;
|
||
|
bool isXpStat = ( statKey == HASH_STAT_ID("MP0_CHAR_XP_FM")
|
||
|
|| statKey == HASH_STAT_ID("MP1_CHAR_XP_FM")
|
||
|
|| statKey == HASH_STAT_ID("MP2_CHAR_XP_FM")
|
||
|
|| statKey == HASH_STAT_ID("MP3_CHAR_XP_FM"));
|
||
|
|
||
|
if(isXpStat)
|
||
|
{
|
||
|
oldValue = pStat->GetInt();
|
||
|
}
|
||
|
|
||
|
bool retHaschangedValue = false;
|
||
|
result = pStat->SetData(pData, sizeofData, retHaschangedValue);
|
||
|
|
||
|
// Debug metric to help track down XP loss - B*2239619
|
||
|
if(result && retHaschangedValue && isXpStat)
|
||
|
{
|
||
|
int newValue = *((int*)pData);
|
||
|
if(newValue<oldValue && oldValue>0)
|
||
|
{
|
||
|
CNetworkTelemetry::AppendXPLoss(oldValue, newValue);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Mark as synched now that we've loaded the authoritative value (only MP stats need to be marked synched)
|
||
|
if (flags&STATUPDATEFLAG_LOAD_FROM_MP_SAVEGAME && statDesc.GetIsOnlineData())
|
||
|
{
|
||
|
statDesc.SetSynched(true);
|
||
|
}
|
||
|
|
||
|
bool forcedStatDataChange = false;
|
||
|
//Force value has changed so that we can get a prof stat flush.
|
||
|
if (!retHaschangedValue && (flags&STATUPDATEFLAG_FORCE_CHANGE))
|
||
|
{
|
||
|
retHaschangedValue = true;
|
||
|
forcedStatDataChange = true;
|
||
|
}
|
||
|
|
||
|
if (result && retHaschangedValue)
|
||
|
{
|
||
|
#if __BANK
|
||
|
if (retHaschangedValue && pStat->DebugStatChanges())
|
||
|
{
|
||
|
char buff[48];
|
||
|
profileStatDebugf("Stat \"%s\" has CHANGED VALUE, id=(\"%d\"), Value=\"%s\"", statIter.GetKeyName(), statIter.GetKey().GetHash(), pStat->ValueToString(buff, 48));
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
|
||
|
//Update stats metrics.
|
||
|
if (!(flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME) && statDesc.GetIsOnlineData() && !forcedStatDataChange)
|
||
|
{
|
||
|
m_StatsMetricsMgr.RegisterStatChanged(statKey);
|
||
|
}
|
||
|
|
||
|
//Set dirty for savegame
|
||
|
if (!(flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME))
|
||
|
{
|
||
|
statDesc.SetDirtySavegame(true);
|
||
|
}
|
||
|
|
||
|
if (!(flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME) && statDesc.GetTriggerEventValueChanged())
|
||
|
{
|
||
|
GetEventGlobalGroup()->Add(CEventStatChangedValue((int)statKey.GetHash()));
|
||
|
}
|
||
|
|
||
|
//Profile stat ready to be synched with ROS.
|
||
|
if (statDesc.GetIsProfileStat() && statDesc.GetSynched() && !(flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME) && !StatsInterface::GetBlockSaveRequests())
|
||
|
{
|
||
|
// Dirty the stat, and dirty the profile too based on the STATUPDATEFLAG_DIRTY_PROFILE flag
|
||
|
pStat->SetDirty((flags&STATUPDATEFLAG_DIRTY_PROFILE) ? true : false);
|
||
|
}
|
||
|
|
||
|
//If we're modifying an SP profile stat (that isn't our SP profile stat timestamp), we need update our profile stat timestamp
|
||
|
if (statDesc.GetIsProfileStat()
|
||
|
&& !(flags&STATUPDATEFLAG_LOAD_FROM_SAVEGAME)
|
||
|
&& statKey != STAT_SP_PROFILE_STAT_VERSION
|
||
|
&& statDesc.GetGroup() >= PS_GRP_SP_START
|
||
|
&& statDesc.GetGroup() <= PS_GRP_SP_END)
|
||
|
{
|
||
|
StatsInterface::SetStatData(STAT_SP_PROFILE_STAT_VERSION, rlGetPosixTime(), STATUPDATEFLAG_ASSERTONLINESTATS);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if __ASSERT
|
||
|
if ((flags&STATUPDATEFLAG_ASSERTONLINESTATS) && statDesc.GetIsOnlineData())
|
||
|
{
|
||
|
if( !NetworkInterface::IsLocalPlayerOnline() && !pStat->GetIsCodeStat() )
|
||
|
{
|
||
|
statDebugf3("Setting Stat %s online data when the player is signed off.", statIter.GetKeyName());
|
||
|
}
|
||
|
|
||
|
if (NetworkInterface::IsLocalPlayerOnline())
|
||
|
{
|
||
|
const u32 saveFileSlot = statDesc.GetCategory();
|
||
|
int currentChar = StatsInterface::GetCurrentMultiplayerCharaterSlot();
|
||
|
++currentChar;
|
||
|
|
||
|
if (!(0 == saveFileSlot || MAX_STAT_SAVE_CATEGORY == saveFileSlot || saveFileSlot == (u32)currentChar || saveFileSlot == CStatsMgr::GetStatsDataMgr().GetSavesMgr().GetOverrideSaveSlotNumber()))
|
||
|
{
|
||
|
if(pStat->GetIsCodeStat())
|
||
|
{
|
||
|
scriptAssertf(0, "%s : CODER Stat %s has changed value but his data wont be saved in the savegame. Reason:\
|
||
|
Stat param \"SaveCategory\" has value \"%d\" but the current character savegame slot is \"%d\""
|
||
|
,CTheScripts::GetCurrentScriptNameAndProgramCounter(), statIter.GetKeyName(), saveFileSlot, currentChar);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
scrThread::PrePrintStackTrace();
|
||
|
scriptAssertf(0, "%s : SCRIPTER Stat %s has changed value but his data wont be saved in the savegame. Reason:\
|
||
|
Stat param \"SaveCategory\" has value \"%d\" but the current character savegame slot is \"%d\""
|
||
|
,CTheScripts::GetCurrentScriptNameAndProgramCounter(), statIter.GetKeyName(), saveFileSlot, currentChar);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
scrThread::PrePrintStackTrace();
|
||
|
}
|
||
|
}
|
||
|
#endif // __ASSERT
|
||
|
}
|
||
|
|
||
|
#if RSG_DURANGO
|
||
|
events_durango::OnStatWrite(statKey, pData,sizeofData, flags);
|
||
|
#endif
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::SetStatData(const StatId& keyHash, void* pData, const unsigned sizeofData, const u32 flags)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
if (!IsStatsDataValid())
|
||
|
return result;
|
||
|
|
||
|
CStatsDataMgr::StatsMap::Iterator statIter;
|
||
|
if (CStatsMgr::GetStatsDataMgr().StatIterFind(keyHash, statIter))
|
||
|
{
|
||
|
DEV_ONLY( if (!statIter.GetKey().IsValid()) statWarningf("Stat key=\"%s : %s\" hash=\"%d : %d\" is NOT VALID", statIter.GetKey().GetName(), keyHash.GetName(), statIter.GetKey().GetHash(), keyHash.GetHash()); )
|
||
|
|
||
|
if (statIter.GetKey().IsValid())
|
||
|
{
|
||
|
result = SetStatIterData(statIter, pData, sizeofData, flags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::GetStatData(const StatId& keyHash, void* pData, const unsigned sizeofData) const
|
||
|
{
|
||
|
DEV_ONLY(if (!IsKeyValid(keyHash)) statWarningf("Stat key=\"%s\" hash=\"%d\" is NOT VALID", keyHash.GetName(), keyHash.GetHash());)
|
||
|
|
||
|
const sStatDataPtr* ppStat = GetStatPtr(keyHash);
|
||
|
const sStatData* pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
if (statVerify(pStat))
|
||
|
{
|
||
|
bool validDataSize = (sizeofData == pStat->GetSizeOfData());
|
||
|
|
||
|
if (pStat->GetType() == STAT_TYPE_USERID)
|
||
|
{
|
||
|
validDataSize = (sizeofData >= pStat->GetSizeOfData() || sizeofData == sizeof(u64));
|
||
|
}
|
||
|
|
||
|
#if __ASSERT
|
||
|
statAssertf(validDataSize, "Invalid buffer size=\"%d\" for stat key=\"%s\", type=\"%s\", size=\"%d\""
|
||
|
,sizeofData
|
||
|
,ppStat->GetKeyName()
|
||
|
,pStat->GetTypeLabel()
|
||
|
,pStat->GetSizeOfData());
|
||
|
#endif // __ASSERT
|
||
|
|
||
|
if (validDataSize)
|
||
|
{
|
||
|
return pStat->GetData(pData, sizeofData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::GetIsBaseType(const StatId& keyHash, const StatType type) const
|
||
|
{
|
||
|
const sStatDataPtr* ppStat = GetStatPtr(keyHash);
|
||
|
const sStatData* pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
return pStat->GetIsBaseType (type);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
u32
|
||
|
CStatsDataMgr::CheckForDirtyProfileStats(CProfileStats::DirtyCache& dirtyCache, const bool force, const bool online)
|
||
|
{
|
||
|
//Important stats are stats with flush priority > RL_PROFILESTATS_MIN_PRIORITY
|
||
|
u32 numImportantStatsDirty = 0;
|
||
|
|
||
|
const unsigned curTime = fwTimer::GetTimeInMilliseconds_NonScaledClipped();
|
||
|
|
||
|
if (s_DirtyStatsCount > 0 && (force || ((curTime - m_CheckForDirtyProfileStats) > CHECKDIRTYSTATS_INTERVAL)))
|
||
|
{
|
||
|
statWarningf("CheckForDirtyProfileStats - called.");
|
||
|
|
||
|
m_CheckForDirtyProfileStats = curTime;
|
||
|
|
||
|
//Lookout for any dirty stats
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
for (;iter != m_aStatsData.End(); ++iter)
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (!pStat)
|
||
|
{
|
||
|
statWarningf("Stat %s - No stat pointer.", iter.GetKeyName());
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
const sStatDescription& desc = pStat->GetDesc();
|
||
|
|
||
|
//not a profile stat
|
||
|
if (!desc.GetIsProfileStat())
|
||
|
continue;
|
||
|
|
||
|
//not Dirty
|
||
|
if (!desc.GetDirty())
|
||
|
continue;
|
||
|
|
||
|
//Exceptions to the rule. These need to be marked as
|
||
|
// dirty since the backend is still going to reset them
|
||
|
if (desc.GetBlockProfileStatFlush())
|
||
|
{
|
||
|
statWarningf("Stat with key='%s' is Blocked to Profile Stats Flushes.", iter.GetKeyName());
|
||
|
|
||
|
if (desc.GetDirty())
|
||
|
pStat->GetDesc().SetDirty( false );
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//Single Player stats can not be flushed during the MP game..
|
||
|
if (online && !desc.GetIsOnlineData() && !desc.GetAlwaysFlush())
|
||
|
{
|
||
|
// Unless it's a profile setting
|
||
|
if(!desc.GetIsProfileSetting())
|
||
|
{
|
||
|
statWarningf("Single player Stat %s - Not valid to Flush in multiplayer.", iter.GetKeyName());
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Multiplayer stats can not be flushed during the SP game.
|
||
|
if (!online && desc.GetIsOnlineData() && !desc.GetAlwaysFlush())
|
||
|
{
|
||
|
statWarningf("Multiplayer Stat %s - Not valid to Flush in Single player.", iter.GetKeyName());
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!desc.GetSynched())
|
||
|
{
|
||
|
statWarningf("Single player Stat %s - stat is not synched.", iter.GetKeyName());
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Add this dirty stat to the dirty set
|
||
|
rlProfileStatsValue dirtyStatValue;
|
||
|
rlProfileDirtyStat dirtyStat;
|
||
|
if (pStat->WriteOutProfileStat(&dirtyStatValue))
|
||
|
{
|
||
|
dirtyStat.Reset(iter.GetKey(), dirtyStatValue, pStat->GetDesc().GetFlushPriority());
|
||
|
|
||
|
#if !__FINAL
|
||
|
if (iter.GetKey() == STAT_MP_PLAYING_TIME && dirtyStatValue.GetInt64() == 0)
|
||
|
statAssertf(0, "STAT_MP_PLAYING_TIME == 0");
|
||
|
#endif // !__FINAL
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (desc.GetIsProfileStat() && desc.GetDirty())
|
||
|
{
|
||
|
pStat->GetDesc().SetDirty( false );
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (desc.GetFlushPriority() > RL_PROFILESTATS_MIN_PRIORITY)
|
||
|
{
|
||
|
numImportantStatsDirty += 1;
|
||
|
}
|
||
|
|
||
|
//Special case for our two SP version stats, which need the lowest/highest priority buckets
|
||
|
bool addResult = false;
|
||
|
if (Unlikely(iter.GetKey() == STAT_SP_PROFILE_STAT_VERSION))
|
||
|
{
|
||
|
//We must always flush the profile stat version, that way we know if profile stats are
|
||
|
//newer. So use the highest priority bin
|
||
|
addResult = dirtyCache.AddDirtyStat(dirtyStat, dirtyCache.GetMaxPriorityBin());
|
||
|
}
|
||
|
else if (Unlikely(iter.GetKey() == STAT_SP_SAVE_TIMESTAMP))
|
||
|
{
|
||
|
//We must only flush the savegame version if all other stats before it can also
|
||
|
//be flushed, that way we know that profile stats has all stats from this savegame
|
||
|
//version
|
||
|
addResult = dirtyCache.AddDirtyStat(dirtyStat, dirtyCache.GetMinPriorityBin());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
addResult = dirtyCache.AddDirtyStat(dirtyStat);
|
||
|
}
|
||
|
|
||
|
if (!addResult)
|
||
|
{
|
||
|
// Logging if we failed to fit this stat in the dirty set
|
||
|
statWarningf("Stat %s - dirtyCache::AddDirtyStat() Failed", iter.GetKeyName());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (numImportantStatsDirty > 0)
|
||
|
{
|
||
|
statWarningf("CheckForDirtyProfileStats - Found < %d > important dirty profile stats.", numImportantStatsDirty);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return numImportantStatsDirty;
|
||
|
}
|
||
|
|
||
|
u32
|
||
|
CStatsDataMgr::GetDirtyStatCount()
|
||
|
{
|
||
|
return s_DirtyStatsCount;
|
||
|
}
|
||
|
|
||
|
u32
|
||
|
CStatsDataMgr::GetDirtyProfileStatCount()
|
||
|
{
|
||
|
return s_DirtyProfileStatsCount;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::HandleEventProfileStats(const rlProfileStatsEvent* evt)
|
||
|
{
|
||
|
if (!rlProfileStats::IsInitialized())
|
||
|
return;
|
||
|
|
||
|
if (!statVerify(evt))
|
||
|
return;
|
||
|
|
||
|
switch (evt->GetId())
|
||
|
{
|
||
|
case RL_PROFILESTATS_EVENT_SYNCHRONIZE_STAT:
|
||
|
{
|
||
|
const rlProfileStatsSynchronizeStatEvent* synchEvt = static_cast< const rlProfileStatsSynchronizeStatEvent * >(evt);
|
||
|
if(statVerify(synchEvt->m_Value))
|
||
|
{
|
||
|
sStatDataPtr* ppStat = GetStatPtr(STAT_ID(synchEvt->m_StatId));
|
||
|
sStatData* pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
|
||
|
if(pStat)
|
||
|
{
|
||
|
if(!pStat->GetDesc().GetIsProfileStat())
|
||
|
{
|
||
|
BANK_ONLY( statErrorf("Synch Failed for stat=\"%s, %d\" - stat is not a profile stat", ppStat->GetKeyName(), (int)ppStat->GetKey()); )
|
||
|
}
|
||
|
else if(!synchEvt->m_Value)
|
||
|
{
|
||
|
BANK_ONLY( statErrorf("Synch Failed for stat=\"%s, %d\" - val is NULL", ppStat->GetKeyName(), (int)ppStat->GetKey()); )
|
||
|
}
|
||
|
|
||
|
bool flushStat = false;
|
||
|
|
||
|
const sStatDescription& desc = pStat->GetDesc();
|
||
|
|
||
|
#if __BANK
|
||
|
//If we're running a consistency check, verify our local value is either dirty or matches the backend value
|
||
|
if (m_SyncConsistencyStatus.Pending())
|
||
|
{
|
||
|
rlProfileStatsValue localValue;
|
||
|
pStat->WriteOutProfileStat(&localValue);
|
||
|
if (localValue != *synchEvt->m_Value)
|
||
|
{
|
||
|
//The stat we just synced doesn't match what was stored on the backend. Make sure that it either
|
||
|
//hasn't been synced yet, or is marked as dirty (i.e. we knew it didn't match)
|
||
|
|
||
|
//We can only do this on stats that we've actually synced with profile stats already. Since we don't
|
||
|
//actually keep track of that, just check whether or not CProfileStats thinks it's synced the group
|
||
|
//this stat is in
|
||
|
const CProfileStats::eSyncType syncType = desc.GetIsOnlineData() ? CProfileStats::PS_SYNC_MP : CProfileStats::PS_SYNC_SP;
|
||
|
|
||
|
if (CLiveManager::GetProfileStatsMgr().Synchronized(syncType) && !desc.GetDirty())
|
||
|
{
|
||
|
profileStatErrorf("Stat %s mismatches backend, but was marked as synched and isn't dirty", ppStat->GetKeyName());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Break so the consistency check doesn't actually affect synched or anything
|
||
|
break;
|
||
|
}
|
||
|
#endif// __BANK
|
||
|
|
||
|
if (desc.GetServerAuthoritative())
|
||
|
{
|
||
|
//Tunable so we can disable this clear.
|
||
|
if (Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("ENABLE_SERVER_AUTHORITATIVE_SP_SYNCHFLAG_CLEAR", 0xCC4ACF55), true))
|
||
|
{
|
||
|
//Clear synch flag for SP server authoritative stats
|
||
|
//before reading the value. Always read in SP server
|
||
|
//Authoritative stats. GetServerAuthoritative.
|
||
|
if (!desc.GetIsOnlineData() && desc.GetSynched())
|
||
|
{
|
||
|
profileStatDebugf("Clear Server Authoritative Synch Flag for SP stat \"%s\".", ppStat->GetKeyName());
|
||
|
pStat->ResynchServerAuthoritative();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pStat->ReadInProfileStat(synchEvt->m_Value, flushStat))
|
||
|
{
|
||
|
statAssert(desc.GetSynched());
|
||
|
BANK_ONLY(char buff[48];)
|
||
|
BANK_ONLY( profileStatDebugf("Synch Server Authoritative stat \"%s\" with Profile stats value, id=(\"%d\"), Value=\"%s\"",
|
||
|
ppStat->GetKeyName(), (int)ppStat->GetKey(), pStat->ValueToString(buff, 48)); )
|
||
|
|
||
|
//Check for Admin award stats.
|
||
|
StatsInterface::CheckForProfileAwardStat(ppStat);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(!pStat->ProfileStatMatches(synchEvt->m_Value))
|
||
|
{
|
||
|
flushStat = true;
|
||
|
|
||
|
if (!pStat->GetIsBaseType(STAT_TYPE_PROFILE_SETTING) && pStat->GetDesc().GetGroup() == PS_GRP_SP_0 && NetworkInterface::IsNetworkOpen())
|
||
|
{
|
||
|
flushStat = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (flushStat && desc.GetIsProfileStat() && desc.GetSynched())
|
||
|
{
|
||
|
//Set profile stat as Dirty, which means its current value needs to be written to the backend.
|
||
|
pStat->GetDesc().SetDirty(true);
|
||
|
|
||
|
BANK_ONLY(char buff[48];)
|
||
|
BANK_ONLY( profileStatDebugf("Synch stat \"%s\" with Profile stats value, id=(\"%d\"), Value=\"%s\"", ppStat->GetKeyName(), (int)ppStat->GetKey(), pStat->ValueToString(buff, 48)); )
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statWarningf("Server Profile stats differ from local stats. New Config needs to be uploaded");
|
||
|
statWarningf("OnReadProfileStat stat with id=\"%d\" doesnt exist.", synchEvt->m_StatId);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case RL_PROFILESTATS_EVENT_SYNCHRONIZE_GROUP:
|
||
|
{
|
||
|
const rlProfileStatsSynchronizeGroupEvent* synchGroupEvt = static_cast< const rlProfileStatsSynchronizeGroupEvent * >(evt);
|
||
|
|
||
|
BANK_ONLY( profileStatDebugf("Group %d was already synced with backend", synchGroupEvt->m_GroupId); )
|
||
|
|
||
|
// Mark any stats in this group as synched with the profile
|
||
|
const eProfileStatsGroups group = static_cast<eProfileStatsGroups>(synchGroupEvt->m_GroupId);
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = NULL;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat
|
||
|
&& pStat->GetDesc().GetIsProfileStat()
|
||
|
&& pStat->GetDesc().GetGroup() == group)
|
||
|
{
|
||
|
// If this stat is authoritative from profile stats, then mark it as synched,
|
||
|
// since the group didn't need to be synced
|
||
|
if (pStat->GetDesc().GetServerAuthoritative())
|
||
|
{
|
||
|
pStat->GetDesc().SetSynched(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
++iter;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case RL_PROFILESTATS_EVENT_GET_LOCAL_VALUE:
|
||
|
{
|
||
|
const rlProfileStatsGetLocalValueEvent* localValueEvt = static_cast< const rlProfileStatsGetLocalValueEvent * >(evt);
|
||
|
if(statVerify(localValueEvt->m_Value))
|
||
|
{
|
||
|
sStatDataPtr* ppStat = GetStatPtr(STAT_ID(localValueEvt->m_StatId));
|
||
|
sStatData* pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
if(pStat)
|
||
|
{
|
||
|
if (pStat->GetDesc().GetSynched())
|
||
|
{
|
||
|
#if __BANK
|
||
|
if (CLiveManager::GetProfileStatsMgr().PendingFlush())
|
||
|
{
|
||
|
char buff[48];
|
||
|
profileStatDebugf("Write stat \"%s\"(\"%d\") to Profile stats server, Value=\"%s\"", ppStat->GetKeyName(), (int)ppStat->GetKey(), pStat->ValueToString(buff, 48));
|
||
|
}
|
||
|
#endif // __BANK
|
||
|
|
||
|
if(!localValueEvt->m_Value)
|
||
|
{
|
||
|
BANK_ONLY( statErrorf("Failed to write profile for stat=\"%s, %d\" - val is NULL", ppStat->GetKeyName(), (int)ppStat->GetKey()); )
|
||
|
}
|
||
|
else if(!pStat->GetDesc().GetIsProfileStat())
|
||
|
{
|
||
|
BANK_ONLY( statErrorf("Failed to write profile for stat=\"%s, %d\" - stat is not a profile stat", ppStat->GetKeyName(), (int)ppStat->GetKey()); )
|
||
|
}
|
||
|
|
||
|
pStat->WriteOutProfileStat(localValueEvt->m_Value);
|
||
|
|
||
|
#if __BLOCK_TAKEOVERS
|
||
|
//Territories stats are blocked from being sent to the server.
|
||
|
if (pStat->GetIsTerritoryStat())
|
||
|
{
|
||
|
BANK_ONLY( profileStatDebugf("Territory Actually Written value \"%s\" (\"%d\") Value = \"-1\"", ppStat->GetKeyName(), (int)ppStat->GetKey()); )
|
||
|
}
|
||
|
#endif // __BLOCK_TAKEOVERS
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statWarningf("Server Profile stats differ from local stats. New Config needs to be uploaded");
|
||
|
statWarningf("OnWriteProfileStats stat with id=\"%d\" doesnt exist.", localValueEvt->m_StatId);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case RL_PROFILESTATS_EVENT_FLUSH_STAT:
|
||
|
{
|
||
|
const rlProfileStatsFlushStatEvent* flushEvt = static_cast<const rlProfileStatsFlushStatEvent *>(evt);
|
||
|
|
||
|
#if __DEV
|
||
|
//Hack because the game client doesnt know about this stat.
|
||
|
static StatId s_saveTransfer("SERVER_TRANSFER_USED");
|
||
|
if (flushEvt->m_StatId == (int)s_saveTransfer.GetHash())
|
||
|
return;
|
||
|
#endif // __DEV
|
||
|
|
||
|
sStatDataPtr *ppStat = GetStatPtr(STAT_ID(flushEvt->m_StatId));
|
||
|
sStatData *pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
|
||
|
// This event is triggered in response to us publishing the stat, so it should be safe to verify that
|
||
|
// this stat is indeed a profile stat.
|
||
|
if (statVerify(pStat && pStat->GetDesc().GetIsProfileStat()))
|
||
|
{
|
||
|
// When a stat is flushed, it should no longer be considered dirty
|
||
|
pStat->GetDesc().SetDirty(false);
|
||
|
|
||
|
BANK_ONLY(char buff[48];)
|
||
|
BANK_ONLY(profileStatDebugf("Flush stat \"%s\", id=(\"%d\"), Value=\"%s\"", ppStat->GetKeyName(), (int)ppStat->GetKey(), pStat->ValueToString(buff, 48)); )
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::UpdateCommunityStatValues()
|
||
|
{
|
||
|
statDebugf3("Update Community stats:");
|
||
|
|
||
|
//Apply community stats data.
|
||
|
const CCommunityStatsDataMgr& communityStats = m_CommunityStats.GetMetadata();
|
||
|
for (const CCommunityStatsData& data : communityStats.m_Data)
|
||
|
{
|
||
|
OnSynchCommunityStats(data.m_StatId.GetHash(), data.m_Value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::OnSynchCommunityStats(const int statId, const float result)
|
||
|
{
|
||
|
sStatDataPtr* ppStat = GetStatPtr(STAT_ID(statId));
|
||
|
sStatData* pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
if (pStat && statVerify(pStat->GetDesc().GetIsCommunityStat()) && statVerify(!pStat->GetDesc().GetIsProfileStat()))
|
||
|
{
|
||
|
if(pStat->GetDesc().GetIsOnlineData())
|
||
|
{
|
||
|
pStat->GetDesc().SetSynched(true);
|
||
|
}
|
||
|
|
||
|
bool retHaschangedValue = false;
|
||
|
float data = result;
|
||
|
pStat->SetData(&data, sizeof(float), retHaschangedValue);
|
||
|
statDebugf3("Synch Community stat \"%s\" value=\"%f\"", ppStat->GetKeyName(), data);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --- Private Methods ----------------------------------------------------------
|
||
|
|
||
|
#if __DEV
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::TestMaskedStats()
|
||
|
{
|
||
|
#if __TEST_MASKED_STAT
|
||
|
|
||
|
//test u64 values.
|
||
|
StatId statU64("TEST_MASKED_U64");
|
||
|
|
||
|
StatsMap::iterator iter = GetStat(statU64);
|
||
|
if (iter != StatIterEnd())
|
||
|
{
|
||
|
sStatData* stat = *iter;
|
||
|
if (stat)
|
||
|
{
|
||
|
stat->SetUInt64(0x8080808080808080); //1000 0000 1000 0000 1000 0000 1000 0000 1000 0000 1000 0000 1000 0000 1000 0000
|
||
|
|
||
|
//Going to pack the value 255 into 8 bytes
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0x5C, 0, 8); //0101 1100 | 0x5C
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0xFF, 8, 8); //1111 1111 | 0xFF
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0x5C, 16, 8);
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0xFF, 24, 8);
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0x5C, 32, 8);
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0xFF, 40, 8);
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0x5C, 48, 8);
|
||
|
StatsInterface::SetMaskedInt(statU64.GetHash(), 0xFF, 56, 8);
|
||
|
|
||
|
int data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 0, 8, 0);
|
||
|
statAssert(data == 0x5C); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 8, 8, 0);
|
||
|
statAssert(data == 0xFF); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 16, 8, 0);
|
||
|
statAssert(data == 0x5C); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 24, 8, 0);
|
||
|
statAssert(data == 0xFF); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 32, 8, 0);
|
||
|
statAssert(data == 0x5C); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 40, 8, 0);
|
||
|
statAssert(data == 0xFF); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 48, 8, 0);
|
||
|
statAssert(data == 0x5C); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statU64.GetHash(), data, 56, 8, 0);
|
||
|
statAssert(data == 0xFF); data = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//test int values.
|
||
|
StatId statInt("TEST_MASKED_INT");
|
||
|
iter = m_aStatsData.find(statInt);
|
||
|
if (iter != StatIterEnd())
|
||
|
{
|
||
|
sStatData* stat = *iter;
|
||
|
if (stat)
|
||
|
{
|
||
|
stat->SetInt(0x80808080); //1000 0000 1000 0000 1000 0000 1000 0000
|
||
|
|
||
|
//Going to pack the value 255 into 8 bytes
|
||
|
StatsInterface::SetMaskedInt(statInt.GetHash(), 0xFF, 0, 8); //0101 1100
|
||
|
StatsInterface::SetMaskedInt(statInt.GetHash(), 0x5C, 8, 8); //1111 1111
|
||
|
StatsInterface::SetMaskedInt(statInt.GetHash(), 0x5C, 16, 8);
|
||
|
StatsInterface::SetMaskedInt(statInt.GetHash(), 0xFF, 24, 8);
|
||
|
|
||
|
int data = 0;
|
||
|
StatsInterface::GetMaskedInt(statInt.GetHash(), data, 0, 8, 0);
|
||
|
statAssert(data == 0xFF); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statInt.GetHash(), data, 8, 8, 0);
|
||
|
statAssert(data == 0x5C); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statInt.GetHash(), data, 16, 8, 0);
|
||
|
statAssert(data == 0x5C); data = 0;
|
||
|
StatsInterface::GetMaskedInt(statInt.GetHash(), data, 24, 8, 0);
|
||
|
statAssert(data == 0xFF); data = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // __TEST_MASKED_STAT
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::TestPosPack(float x, float y, float z, const char* statName)
|
||
|
{
|
||
|
statDebugf1(" ");
|
||
|
statDebugf1(" ");
|
||
|
|
||
|
statDebugf1("Start TestPosPack");
|
||
|
statDebugf1("... Testing pos=\"x=%f, y=%f, z=%f\" for stat=\"%s\"", x, y, z, statName);
|
||
|
|
||
|
sStatData* statData = GetStat(StatId(statName));
|
||
|
if (statData)
|
||
|
{
|
||
|
u64 prevValue = statData->GetUInt64();
|
||
|
|
||
|
bool retHaschangedValue = false;
|
||
|
|
||
|
u64 value = StatsInterface::PackPos(x, y, z);
|
||
|
statData->SetData(&value, sizeof(u64), retHaschangedValue);
|
||
|
statData->ToString();
|
||
|
|
||
|
x = y = z = 0.0f;
|
||
|
|
||
|
StatsInterface::UnPackPos(statData->GetUInt64(), x, y, z);
|
||
|
statDebugf1("... Result pos=\"x=%f, y=%f, z=%f\"", x, y, z);
|
||
|
|
||
|
statData->SetData(&prevValue, sizeof(u64), retHaschangedValue);
|
||
|
}
|
||
|
|
||
|
statDebugf1("End TestPosPack");
|
||
|
};
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::TestDatePack(int year , int month , int day , int hour , int min , int sec , int mill, const char* statName)
|
||
|
{
|
||
|
statDebugf1(" ");
|
||
|
statDebugf1(" ");
|
||
|
|
||
|
statDebugf1("Start TestDatePack");
|
||
|
statDebugf1("... Testing Date= \"year=%d, month=%d, day=%d, hour=%d, min=%d, sec%d, mill=%d\" for stat=\"%s\"", year , month , day , hour , min , sec , mill, statName);
|
||
|
|
||
|
{
|
||
|
sStatData* statData = GetStat(StatId(statName));
|
||
|
|
||
|
if (statData)
|
||
|
{
|
||
|
u64 prevValue = statData->GetUInt64();
|
||
|
|
||
|
bool retHaschangedValue = false;
|
||
|
|
||
|
u64 value = StatsInterface::PackDate(year , month , day , hour , min , sec , mill);
|
||
|
statData->SetData(&value, sizeof(u64), retHaschangedValue);
|
||
|
statData->ToString();
|
||
|
|
||
|
year = month = day = hour = min = sec = mill = 0;
|
||
|
|
||
|
StatsInterface::UnPackDate(statData->GetUInt64(), year , month , day , hour , min , sec , mill);
|
||
|
statData->ToString();
|
||
|
|
||
|
statDebugf1("... Result Testing Date= \"year=%d, month=%d, day=%d, hour=%d, min=%d, sec=%d, mill=%d\"", year , month , day , hour , min , sec , mill);
|
||
|
|
||
|
statData->SetData(&prevValue, sizeof(u64), retHaschangedValue);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
statDebugf1("End TestDatePack");
|
||
|
};
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::ValidateStaticStatsIds() const
|
||
|
{
|
||
|
#if __ASSERT
|
||
|
|
||
|
//Test User Id stats
|
||
|
sStatDescription desc;
|
||
|
sUserIdStatData statUserId("STAT_USER_ID", desc);
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
sUserIdStatData statUserIdNext("STAT_USER_ID_NEXT", desc);
|
||
|
statUserId.m_NextUserId = &statUserIdNext;
|
||
|
|
||
|
if (rlGamerHandle::IsUsingAccountIdAsKey())
|
||
|
{
|
||
|
statUserId.SetUserId("2535285330439972");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statUserId.SetUserId("rsnmax_whatwhat");
|
||
|
}
|
||
|
#else
|
||
|
statUserId.SetUserId("2535285330439972");
|
||
|
#endif
|
||
|
|
||
|
char userStatBuffer[128];
|
||
|
statUserId.GetUserId(userStatBuffer, 128);
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (rlGamerHandle::IsUsingAccountIdAsKey())
|
||
|
{
|
||
|
statAssert(stricmp(userStatBuffer, "2535285330439972") == 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statAssert(stricmp(userStatBuffer, "rsnmax_whatwhat") == 0);
|
||
|
}
|
||
|
#else
|
||
|
statAssert(stricmp(userStatBuffer, "2535285330439972") == 0);
|
||
|
#endif
|
||
|
|
||
|
StatId_static* sid = StatId_static::sm_First;
|
||
|
while(sid)
|
||
|
{
|
||
|
statAssertf(StatsInterface::IsKeyValid(sid->GetHash()), "CODER Stat id=\"%d\" with name=\"%s\" does not exist.", sid->GetHash(), sid->GetName());
|
||
|
sid = sid->m_Next;
|
||
|
}
|
||
|
|
||
|
StatId_char* sCharId = StatId_char::sm_First;
|
||
|
while(sCharId)
|
||
|
{
|
||
|
if (sCharId->m_Validate)
|
||
|
{
|
||
|
statAssertf(sCharId->m_Name, "Failed to valid stat because the name is NULL");
|
||
|
if (sCharId->m_Name)
|
||
|
{
|
||
|
const bool isSpKeyValid = (StatsInterface::IsKeyValid(sCharId->GetHash(CHAR_MICHEAL)) &&
|
||
|
StatsInterface::IsKeyValid(sCharId->GetHash(CHAR_FRANKLIN)) &&
|
||
|
StatsInterface::IsKeyValid(sCharId->GetHash(CHAR_TREVOR)));
|
||
|
|
||
|
bool isMpKeyValid = true;
|
||
|
for (int i=0; i<MAX_NUM_MP_CHARS && isMpKeyValid; i++)
|
||
|
isMpKeyValid &= StatsInterface::IsKeyValid(sCharId->GetHash(CHAR_MP0+i));
|
||
|
|
||
|
statAssertf(isSpKeyValid || isMpKeyValid, "CODER Stat \"%s\" does not exist.", sCharId->m_Name);
|
||
|
|
||
|
if (!isSpKeyValid && isMpKeyValid)
|
||
|
{
|
||
|
statDebugf1("CODER Stat \"%s\" is multiplayer only", sCharId->m_Name);
|
||
|
}
|
||
|
else if (!isMpKeyValid && isSpKeyValid)
|
||
|
{
|
||
|
statDebugf1("CODER Stat \"%s\" is single player only", sCharId->m_Name);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sCharId = sCharId->m_Next;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::LoadDataXMLFile(const char* pFileName, bool bSinglePlayer)
|
||
|
{
|
||
|
atHashString filenameHash(pFileName);
|
||
|
if (!m_ProcessedFiles.Access(filenameHash))
|
||
|
{
|
||
|
if (statVerify(pFileName))
|
||
|
{
|
||
|
parTree* pTree = PARSER.LoadTree(pFileName, "xml");
|
||
|
if(pTree)
|
||
|
{
|
||
|
statDebugf1("-----------------------------------------------------------------");
|
||
|
statDebugf1("LoadDataXMLFile - FileName \"%s\"", pFileName);
|
||
|
statDebugf1("-----------------------------------------------------------------");
|
||
|
|
||
|
// --- Start Parsing ----------------------------------------------------------
|
||
|
parTreeNode* pRootNode = pTree->GetRoot();
|
||
|
if(pRootNode)
|
||
|
{
|
||
|
parTreeNode::ChildNodeIterator it = pRootNode->BeginChildren();
|
||
|
for(; it != pRootNode->EndChildren(); ++it)
|
||
|
{
|
||
|
// --- All stats definitions ----------------------------------------------------------
|
||
|
if(stricmp((*it)->GetElement().GetName(), "stats") == 0)
|
||
|
{
|
||
|
LoadStatsData((*it), bSinglePlayer);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// --- End Parsing ----------------------------------------------------------
|
||
|
|
||
|
delete pTree;
|
||
|
|
||
|
//statDebugf2("-----------------------------------------------------------------");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statErrorf("LoadDataXMLFile - FileName \"%s\" - pTree NULL.", pFileName);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statErrorf("LoadDataXMLFile - NULL FileName.");
|
||
|
}
|
||
|
|
||
|
m_ProcessedFiles.Insert(filenameHash,true);
|
||
|
DEV_ONLY(if (m_aStatsData.GetCount() == 0) statWarningf("Single Player stats are empty");)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statDebugf1("LoadDataXMLFile - FileName \"%s\" Already Processed.", pFileName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::LoadStatsData(parTreeNode* pNode, bool bSinglePlayer)
|
||
|
{
|
||
|
if(pNode)
|
||
|
{
|
||
|
parTreeNode::ChildNodeIterator it = pNode->BeginChildren();
|
||
|
for(; it != pNode->EndChildren(); ++it)
|
||
|
{
|
||
|
strStreamingEngine::GetLoader().CallKeepAliveCallbackIfNecessary();
|
||
|
if(stricmp((*it)->GetElement().GetName(), "stat") == 0)
|
||
|
{
|
||
|
Assertf((*it)->GetElement().FindAttribute("Name"), "Missing attribute \"Name\"");
|
||
|
Assertf((*it)->GetElement().FindAttribute("Type"), "Missing attribute \"Type\"");
|
||
|
Assertf((*it)->GetElement().FindAttribute("Comment"), "Missing attribute \"Comment\"");
|
||
|
|
||
|
if ((*it)->GetElement().FindAttribute("Name") && (*it)->GetElement().FindAttribute("Type"))
|
||
|
{
|
||
|
const char* pStatIdName = (*it)->GetElement().FindAttributeAnyCase("Name")->GetStringValue();
|
||
|
const char* pStatType = (*it)->GetElement().FindAttributeAnyCase("Type")->GetStringValue();
|
||
|
|
||
|
const char* pStatOwner = "coder";
|
||
|
if ((*it)->GetElement().FindAttribute("Owner"))
|
||
|
{
|
||
|
pStatOwner = (*it)->GetElement().FindAttributeAnyCase("Owner")->GetStringValue();
|
||
|
}
|
||
|
|
||
|
if ((*it)->GetElement().FindAttribute("Flag"))
|
||
|
{
|
||
|
const char* pFlag = (*it)->GetElement().FindAttributeAnyCase("Flag")->GetStringValue();
|
||
|
if (pFlag && istrlen(pFlag) > 0 && ATSTRINGHASH("FEATURE_GEN9_EXCLUSIVE", 0x7AFBE5A2) == atHashString(pFlag))
|
||
|
{
|
||
|
#if !IS_GEN9_PLATFORM
|
||
|
statDebugf1(" SKIP ADDDING GEN9 STAT '%s' FLAG 'FEATURE_GEN9_EXCLUSIVE' PRESENT.", pStatIdName);
|
||
|
//Next Gen Stat. Skip.
|
||
|
continue;
|
||
|
#endif //!IS_GEN9_PLATFORM
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char* pStatMultiHash = 0;
|
||
|
if ((*it)->GetElement().FindAttribute("Multihash"))
|
||
|
{
|
||
|
pStatMultiHash = (*it)->GetElement().FindAttributeAnyCase("Multihash")->GetStringValue();
|
||
|
}
|
||
|
|
||
|
const char* pStatPacked = 0;
|
||
|
if ((*it)->GetElement().FindAttribute("packed"))
|
||
|
{
|
||
|
pStatPacked = (*it)->GetElement().FindAttributeAnyCase("packed")->GetStringValue();
|
||
|
}
|
||
|
|
||
|
const bool typeIsValid = (STAT_TYPE_NONE != GET_TYPE_FROM_NAME(pStatType));
|
||
|
statAssertf(typeIsValid, "Invalid type for stat=\"%s\" type=\"%s\"", pStatIdName, pStatType);
|
||
|
|
||
|
if (!statVerify(pStatIdName) || !statVerify(pStatType) || !typeIsValid)
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
##########################################
|
||
|
##########################################
|
||
|
|
||
|
LOCAL USER STORAGE
|
||
|
|
||
|
##########################################
|
||
|
##########################################
|
||
|
*/
|
||
|
|
||
|
#if RSG_PC
|
||
|
if ((*it)->GetElement().FindAttributeBoolValue("adminstat", false))
|
||
|
{
|
||
|
OUTPUT_ONLY( StatId statIgnored(pStatIdName); )
|
||
|
gnetWarning("IGNORED ADMIN STAT - name='%s', hash='%d'.", pStatIdName, statIgnored.GetHash());
|
||
|
continue;
|
||
|
}
|
||
|
#endif // RSG_PC
|
||
|
|
||
|
if ((*it)->GetElement().FindAttributeBoolValue("ProfileSettings", false))
|
||
|
{
|
||
|
statAssertf((*it)->GetElement().FindAttributeBoolValue("profile", false),
|
||
|
"Stat %s set as profile setting but its not set as a profile stat. fix that shit.", pStatIdName);
|
||
|
}
|
||
|
|
||
|
const bool isProfileSettings = (*it)->GetElement().FindAttribute("ProfileSettingId") ? true : false;
|
||
|
const bool alwaysFlush = (*it)->GetElement().FindAttribute("alwaysFlush") ? true : false;
|
||
|
|
||
|
sStatDescription desc((*it)->GetElement().FindAttributeBoolValue("online", false)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("community", false)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("profile", false)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("ServerAuthoritative", false)
|
||
|
,(u32)(*it)->GetElement().FindAttributeIntValue("ReadPriority", 0)
|
||
|
,(u32)(*it)->GetElement().FindAttributeIntValue("FlushPriority", 0)
|
||
|
,(u32)(*it)->GetElement().FindAttributeIntValue("SaveCategory", 0)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("characterStat", false)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("triggerEventValueChanged", false)
|
||
|
,isProfileSettings
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("BlockReset", false)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("BlockProfileStatFlush", false)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("BlockSynchEventFlush", false)
|
||
|
,(*it)->GetElement().FindAttributeBoolValue("NoSaveStat", false)
|
||
|
, alwaysFlush);
|
||
|
|
||
|
#if __MPCLOUDSAVES_ONLY_ONEFILE
|
||
|
if (0 < desc.GetCategory())
|
||
|
statErrorf("All 'SaveCategory' must be set to '0'. Reverting xml stats '%s' setup file setting.", pStatIdName);
|
||
|
desc.SetCategory(0);
|
||
|
#endif // __MPCLOUDSAVES_ONLY_ONEFILE
|
||
|
|
||
|
sStatParser statparser;
|
||
|
statparser.m_Name = pStatIdName;
|
||
|
statparser.m_Multihash = pStatMultiHash;
|
||
|
statparser.m_Owner = pStatOwner;
|
||
|
statparser.m_NumBitsPerValue = GET_NUMBER_OF_BITS_FROM_NAME(pStatPacked);
|
||
|
statparser.m_Desc = desc;
|
||
|
statparser.m_Type = GET_TYPE_FROM_NAME(pStatType);
|
||
|
statparser.m_RawType = pStatType;
|
||
|
statparser.m_Node = (*it);
|
||
|
statparser.m_ProfileSettingId = (u16)(*it)->GetElement().FindAttributeIntValue("ProfileSettingId", 0xFFFF);
|
||
|
//If the stat is tagged with metric flag
|
||
|
statparser.m_MetricSettingId = (u32)(*it)->GetElement().FindAttributeIntValue("Flag1", 0, false);
|
||
|
|
||
|
statparser.m_GroupIsSet = false;
|
||
|
|
||
|
#if !__FINAL
|
||
|
if (statparser.m_Desc.GetIsOnlineData() && statparser.m_Desc.GetCategory() > 0 && !statparser.m_Desc.GetIsCharacterStat())
|
||
|
{
|
||
|
statWarningf("All 'SaveCategory' must be set to '0' for non character stats, stat named '%s' setup is wrong.", pStatIdName);
|
||
|
}
|
||
|
#endif // !__FINAL
|
||
|
|
||
|
//If a group was specified in the config, then always use it. Otherwise this will be generated based on
|
||
|
//whether or not this is a character-specific stat, as well as the name of the stat.
|
||
|
if ((*it)->GetElement().FindAttribute("Group"))
|
||
|
{
|
||
|
statparser.m_GroupIsSet = true;
|
||
|
statparser.m_Desc.SetGroup(static_cast<eProfileStatsGroups>((*it)->GetElement().FindAttributeIntValue("Group", 0)));
|
||
|
|
||
|
#if __ASSERT
|
||
|
eProfileStatsGroups grp = statparser.m_Desc.GetGroup();
|
||
|
if (desc.GetIsOnlineData())
|
||
|
statAssertf(grp<=PS_GRP_MP_END, "Invalid Group set in stat=%s, grp=%d", statparser.m_Name.c_str(), grp);
|
||
|
else
|
||
|
statAssertf(grp>=PS_GRP_SP_START && grp<=PS_GRP_SP_END, "Invalid Group set in stat=%s, grp=%d", statparser.m_Name.c_str(), grp);
|
||
|
#endif // __ASSERT
|
||
|
}
|
||
|
|
||
|
//This is a stat unique per character
|
||
|
if ((*it)->GetElement().FindAttributeBoolValue("characterStat", false))
|
||
|
{
|
||
|
// If this flag is set, we want to track a stat for each individual characters.
|
||
|
RegisterNewStatForEachCharacter(statparser, bSinglePlayer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Check that the stat has correct size. Otherwise it trims its name silently, screwing anything relying on that stat
|
||
|
statAssertf(strlen(statparser.m_Name.c_str()) < MAX_STAT_LABEL_SIZE, "Stat name %s is too long, please shorten it by %" I64FMT "d chars", statparser.m_Name.c_str(), strlen(statparser.m_Name.c_str()) - MAX_STAT_LABEL_SIZE + 1);
|
||
|
RegisterNewStat(statparser);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::RegisterNewStat(sStatParser& statparser)
|
||
|
{
|
||
|
statAssert(statparser.m_Name.c_str());
|
||
|
statAssert(statparser.m_Owner.c_str());
|
||
|
statAssert(statparser.m_Node);
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
// Load Assert on assumptions
|
||
|
statAssertf((stricmp(statparser.m_Owner.c_str(), "system") == 0) || (stricmp(statparser.m_Owner.c_str(), "coder") == 0) || (stricmp(statparser.m_Owner.c_str(), "script") == 0),
|
||
|
"Stat %s doesnt have a valid owner set values must be \"system\", \"coder\" or \"script\".", statparser.m_Name.c_str());
|
||
|
if (statparser.m_Desc.GetIsProfileStat()) statAssertf(!statparser.m_Desc.GetIsCommunityStat(), "Failed for %s - (profileStat)", statparser.m_Name.c_str());
|
||
|
if (statparser.m_Desc.GetDownloadPriority() > 0) statAssertf(statparser.m_Desc.GetIsOnlineData(), "Failed for %s - (statparser.m_Desc.GetDownloadPriority() > 0)", statparser.m_Name.c_str());
|
||
|
if (statparser.m_Desc.GetDownloadPriority() > 0) statAssertf(statparser.m_Desc.GetIsProfileStat(), "Failed for %s - (statparser.m_Desc.GetDownloadPriority() > 0)", statparser.m_Name.c_str());
|
||
|
if (statparser.m_Desc.GetFlushPriority() > 0) statAssertf(statparser.m_Desc.GetIsProfileStat(), "Failed for %s - (flushPriority > 0)", statparser.m_Name.c_str());
|
||
|
if (statparser.m_Desc.GetServerAuthoritative()) statAssertf(statparser.m_Desc.GetIsProfileStat(), "Failed for %s - (GetServerAuthoritative())", statparser.m_Name.c_str());
|
||
|
if (statparser.m_Desc.GetIsCommunityStat()) statAssertf(!statparser.m_Desc.GetIsProfileStat(), "Failed for %s - (GetIsCommunityStat())", statparser.m_Name.c_str());
|
||
|
if (statparser.m_Desc.GetIsCommunityStat()) statAssertf(!statparser.m_Desc.GetIsOnlineData(), "Failed for %s - (GetIsCommunityStat())", statparser.m_Name.c_str());
|
||
|
if (statparser.m_NumBitsPerValue > 0) statAssertf(statparser.m_Type == STAT_TYPE_PACKED, "Failed for %s - (Packed stat with wrong type - must be u64)", statparser.m_Name.c_str());
|
||
|
if (statparser.m_ProfileSettingId != 0xFFFF) statAssertf(statparser.m_Type == STAT_TYPE_PROFILE_SETTING, "Failed for %s - (Profile setting stat with wrong type - must be profilesetting)", statparser.m_Name.c_str());
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
|
||
|
sStatData* pStatSlot = 0;
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
sStatData* pStatNextSlot = 0;
|
||
|
#endif
|
||
|
|
||
|
//Figure out our group based on our name, unless one was explicitly present in the config
|
||
|
//For character stats, this gets set in RegisterNewStatForEachCharacter
|
||
|
if (!statparser.m_Desc.GetIsCharacterStat()
|
||
|
&& !statparser.m_GroupIsSet)
|
||
|
{
|
||
|
if (statparser.m_Desc.GetIsOnlineData())
|
||
|
{
|
||
|
const char* pName = statparser.m_Name.c_str();
|
||
|
if (statVerify(pName))
|
||
|
{
|
||
|
//MP0_, MP1_, MP2_, MP3_
|
||
|
if ('M' == pName[0] && 'P' == pName[1] && isdigit(pName[2]) && '_' == pName[3])
|
||
|
{
|
||
|
const int digit = atoi(&pName[2]);
|
||
|
Assert(digit >= 0 && digit <= 3);
|
||
|
statparser.m_Desc.SetGroup(static_cast<eProfileStatsGroups>(PS_GRP_MP_1 + digit));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statparser.m_Desc.SetGroup(PS_GRP_MP_6);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statparser.m_Desc.SetGroup(PS_GRP_SP_0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Support obfuscated stat types - fake a type ID of the existing ID + MAX_STAT_TYPE
|
||
|
u32 statTypeIncObfuscated = statparser.m_Type;
|
||
|
if(stricmp(statparser.m_RawType, "short") == 0 ||
|
||
|
stricmp(statparser.m_RawType, "double") == 0 ||
|
||
|
stricmp(statparser.m_RawType, "x64") == 0 ||
|
||
|
stricmp(statparser.m_RawType, "long") == 0)
|
||
|
{
|
||
|
statTypeIncObfuscated += MAX_STAT_TYPE;
|
||
|
#if __ASSERT
|
||
|
++m_numObfuscatedStatTypes;
|
||
|
#endif // __ASSERT
|
||
|
}
|
||
|
switch (statTypeIncObfuscated)
|
||
|
{
|
||
|
case STAT_TYPE_TEXTLABEL: pStatSlot = rage_new sLabelStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_INT: pStatSlot = rage_new sIntStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_FLOAT: pStatSlot = rage_new sFloatStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_BOOLEAN: pStatSlot = rage_new sBoolStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_UINT8: pStatSlot = rage_new sUns8StatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_UINT16: pStatSlot = rage_new sUns16StatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_UINT32: pStatSlot = rage_new sUns32StatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_DATE: pStatSlot = rage_new sDateStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_POS: pStatSlot = rage_new sPosStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_STRING: pStatSlot = rage_new sStringStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_UINT64: pStatSlot = rage_new sUns64StatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_PACKED: pStatSlot = rage_new sPackedStatData(statparser.m_Name.c_str(), statparser.m_Desc, statparser.m_NumBitsPerValue); statAssert(pStatSlot && ((sPackedStatData*)pStatSlot)->NumberOfBitsIsValid()); break;
|
||
|
case STAT_TYPE_USERID: pStatSlot = rage_new sUserIdStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case STAT_TYPE_PROFILE_SETTING: pStatSlot = rage_new sProfileSettingStatData(statparser.m_Name.c_str(), statparser.m_Desc, statparser.m_ProfileSettingId); break;
|
||
|
case STAT_TYPE_INT64: pStatSlot = rage_new sInt64StatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
|
||
|
// Obfuscated types:
|
||
|
case MAX_STAT_TYPE+STAT_TYPE_INT: pStatSlot = rage_new sObfIntStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case MAX_STAT_TYPE+STAT_TYPE_FLOAT: pStatSlot = rage_new sObfFloatStatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case MAX_STAT_TYPE+STAT_TYPE_UINT64: pStatSlot = rage_new sObfUns64StatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
case MAX_STAT_TYPE+STAT_TYPE_INT64: pStatSlot = rage_new sObfInt64StatData(statparser.m_Name.c_str(), statparser.m_Desc); break;
|
||
|
|
||
|
default: statAssertf(0, "Invalid stat %s with type \"%d\"", statparser.m_Name.c_str(), statparser.m_Type); break;
|
||
|
}
|
||
|
|
||
|
if (pStatSlot)
|
||
|
{
|
||
|
sStatDataPtr newStatPtr(pStatSlot);
|
||
|
newStatPtr.SetKey(statparser.m_Name.c_str());
|
||
|
|
||
|
if (statparser.m_Multihash.c_str())
|
||
|
{
|
||
|
char szName[MAX_STAT_LABEL_SIZE];
|
||
|
snprintf(szName, MAX_STAT_LABEL_SIZE, "%s%s", statparser.m_Name.c_str(), statparser.m_Multihash.c_str());
|
||
|
newStatPtr.SetKeyHash(atStringHash(statparser.m_Multihash.c_str(), atStringHash(statparser.m_Name.c_str())), szName);
|
||
|
statAssert(newStatPtr.GetKeyName());
|
||
|
}
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
//Setup the pointer to next stat because USER Id stat's have to be split in 2 u64 to hold
|
||
|
// up to 16 chars in PSN.
|
||
|
if (statparser.m_Type == STAT_TYPE_USERID)
|
||
|
{
|
||
|
char szName[MAX_STAT_LABEL_SIZE];
|
||
|
snprintf(szName, MAX_STAT_LABEL_SIZE, "%s_NEXT", statparser.m_Name.c_str());
|
||
|
pStatNextSlot = rage_new sUserIdStatData(szName, statparser.m_Desc);
|
||
|
|
||
|
if (statVerify(pStatNextSlot))
|
||
|
{
|
||
|
((sUserIdStatData*)pStatSlot)->m_NextUserId = static_cast<sUserIdStatData*>(pStatNextSlot);
|
||
|
}
|
||
|
}
|
||
|
#endif // RLGAMERHANDLE_NP
|
||
|
|
||
|
if (statparser.m_Node->GetElement().FindAttribute("Default"))
|
||
|
{
|
||
|
switch (statparser.m_Type)
|
||
|
{
|
||
|
case STAT_TYPE_STRING:
|
||
|
{
|
||
|
char* text = const_cast<char*>(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->GetStringValue());
|
||
|
if (text && 0 < strlen(text))
|
||
|
{
|
||
|
pStatSlot->SetString(text);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_TEXTLABEL:
|
||
|
{
|
||
|
const char* text = statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->GetStringValue();
|
||
|
if (text && 0 < strlen(text) && TheText.DoesTextLabelExist(text))
|
||
|
{
|
||
|
pStatSlot->SetInt(atStringHash(text));
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_INT:
|
||
|
{
|
||
|
pStatSlot->SetInt(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->FindIntValue());
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_FLOAT:
|
||
|
{
|
||
|
pStatSlot->SetFloat(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->FindFloatValue());
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_BOOLEAN:
|
||
|
{
|
||
|
bool bvalue = false;
|
||
|
|
||
|
const char* defaultValue = statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->GetStringValue();
|
||
|
|
||
|
if (AssertVerify(defaultValue) && stricmp(defaultValue, "true") == 0)
|
||
|
bvalue = true;
|
||
|
|
||
|
pStatSlot->SetBoolean(bvalue);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_UINT8:
|
||
|
{
|
||
|
pStatSlot->SetUInt8((u8)(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->FindIntValue()));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_UINT16:
|
||
|
{
|
||
|
pStatSlot->SetUInt16((u16)(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->FindIntValue()));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_UINT32:
|
||
|
{
|
||
|
pStatSlot->SetUInt32((u32)(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->FindIntValue()));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_INT64:
|
||
|
{
|
||
|
pStatSlot->SetInt64((s64)(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->FindIntValue()));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_UINT64:
|
||
|
case STAT_TYPE_DATE:
|
||
|
case STAT_TYPE_POS:
|
||
|
case STAT_TYPE_PACKED:
|
||
|
{
|
||
|
pStatSlot->SetUInt64((u64)(statparser.m_Node->GetElement().FindAttributeAnyCase("Default")->FindIntValue()));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_PROFILE_SETTING:
|
||
|
case STAT_TYPE_USERID:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
statAssertf(0, "Stat %s Invalid type=\"%s:%d\"", newStatPtr.GetKeyName(), pStatSlot->GetTypeLabel(), pStatSlot->GetType());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NOTFINAL_ONLY( pStatSlot->SetOwner(stricmp(statparser.m_Owner, "coder") == 0); )
|
||
|
|
||
|
//Silently fail to add the stat if already exists - due to 360/ps3 cRc's this can happen.
|
||
|
if (statVerify(newStatPtr.GetKey().IsValid()) && !IsKeyValid(newStatPtr.GetKey()))
|
||
|
{
|
||
|
//Insert stat in StatsMap
|
||
|
m_aStatsData.Insert(newStatPtr);
|
||
|
|
||
|
//Register stat for metric
|
||
|
m_StatsMetricsMgr.RegisterNewStatMetric(statparser, newStatPtr.GetKey());
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
//Insert second stat in StatsMap
|
||
|
if (pStatNextSlot)
|
||
|
{
|
||
|
char szName[MAX_STAT_LABEL_SIZE];
|
||
|
snprintf(szName, MAX_STAT_LABEL_SIZE, "%s_NEXT", statparser.m_Name.c_str());
|
||
|
sStatDataPtr nextStatPtr(pStatNextSlot);
|
||
|
nextStatPtr.SetKey(szName);
|
||
|
m_aStatsData.Insert(nextStatPtr);
|
||
|
}
|
||
|
#endif // RLGAMERHANDLE_NP
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pStatSlot)
|
||
|
{
|
||
|
delete pStatSlot;
|
||
|
}
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (pStatNextSlot)
|
||
|
{
|
||
|
delete pStatNextSlot;
|
||
|
}
|
||
|
#endif // RLGAMERHANDLE_NP
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::AddNewStat(const char* name, sStatDescription& desc, const StatType& type, const u16 profileSettingId)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
statAssert(name);
|
||
|
if (!name)
|
||
|
return result;
|
||
|
|
||
|
StatId stat(name);
|
||
|
statAssertf(!IsKeyValid(stat), "Stat with key %s already exists.", name);
|
||
|
if (IsKeyValid(stat))
|
||
|
return result;
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
// Load Assert on assumptions
|
||
|
|
||
|
if (desc.GetIsProfileStat()) statAssertf(!desc.GetIsCommunityStat(), "Failed for %s - (profileStat)", name);
|
||
|
if (desc.GetDownloadPriority() > 0) statAssertf( desc.GetIsOnlineData(), "Failed for %s - (desc.GetDownloadPriority() > 0)", name);
|
||
|
if (desc.GetDownloadPriority() > 0) statAssertf( desc.GetIsProfileStat(), "Failed for %s - (desc.GetDownloadPriority() > 0)", name);
|
||
|
if (desc.GetFlushPriority() > 0) statAssertf( desc.GetIsProfileStat(), "Failed for %s - (flushPriority > 0)", name);
|
||
|
if (desc.GetServerAuthoritative()) statAssertf( desc.GetIsProfileStat(), "Failed for %s - (GetServerAuthoritative())", name);
|
||
|
if (desc.GetIsCommunityStat()) statAssertf(!desc.GetIsProfileStat(), "Failed for %s - (GetIsCommunityStat())", name);
|
||
|
if (desc.GetIsCommunityStat()) statAssertf(!desc.GetIsOnlineData(), "Failed for %s - (GetIsCommunityStat())", name);
|
||
|
if (desc.GetIsNoSaveStat()) statAssertf( !desc.GetIsProfileStat(), "Failed for %s - (GetIsNoSaveStat()) - should not be a profile stat", name);
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------
|
||
|
|
||
|
sStatData* pStatSlot = 0;
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
sStatData* pStatNextSlot = 0;
|
||
|
#endif
|
||
|
|
||
|
switch (type)
|
||
|
{
|
||
|
case STAT_TYPE_TEXTLABEL: pStatSlot = rage_new sLabelStatData(name, desc); break;
|
||
|
case STAT_TYPE_INT: pStatSlot = rage_new sIntStatData(name, desc); break;
|
||
|
case STAT_TYPE_FLOAT: pStatSlot = rage_new sFloatStatData(name, desc); break;
|
||
|
case STAT_TYPE_BOOLEAN: pStatSlot = rage_new sBoolStatData(name, desc); break;
|
||
|
case STAT_TYPE_UINT8: pStatSlot = rage_new sUns8StatData(name, desc); break;
|
||
|
case STAT_TYPE_UINT16: pStatSlot = rage_new sUns16StatData(name, desc); break;
|
||
|
case STAT_TYPE_UINT32: pStatSlot = rage_new sUns32StatData(name, desc); break;
|
||
|
case STAT_TYPE_DATE: pStatSlot = rage_new sDateStatData(name, desc); break;
|
||
|
case STAT_TYPE_POS: pStatSlot = rage_new sPosStatData(name, desc); break;
|
||
|
case STAT_TYPE_STRING: pStatSlot = rage_new sStringStatData(name, desc); break;
|
||
|
case STAT_TYPE_UINT64: pStatSlot = rage_new sUns64StatData(name, desc); break;
|
||
|
case STAT_TYPE_PACKED: pStatSlot = rage_new sPackedStatData(name, desc, 8); statAssert(pStatSlot && ((sPackedStatData*)pStatSlot)->NumberOfBitsIsValid()); break;
|
||
|
case STAT_TYPE_USERID: pStatSlot = rage_new sUserIdStatData(name, desc); break;
|
||
|
case STAT_TYPE_PROFILE_SETTING: pStatSlot = rage_new sProfileSettingStatData(name, desc, profileSettingId); break;
|
||
|
case STAT_TYPE_INT64: pStatSlot = rage_new sInt64StatData(name, desc); break;
|
||
|
|
||
|
default: statAssertf(0, "Invalid stat %s with type \"%d\"", name, type); break;
|
||
|
}
|
||
|
|
||
|
if (pStatSlot)
|
||
|
{
|
||
|
sStatDataPtr newStatPtr(pStatSlot);
|
||
|
newStatPtr.SetKey(name);
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
//Setup the pointer to next stat because USER Id stat's have to be split in 2 u64 to hold
|
||
|
// up to 16 chars in PSN.
|
||
|
if (type == STAT_TYPE_USERID)
|
||
|
{
|
||
|
char szName[MAX_STAT_LABEL_SIZE];
|
||
|
snprintf(szName, MAX_STAT_LABEL_SIZE, "%s_NEXT", name);
|
||
|
pStatNextSlot = rage_new sUserIdStatData(szName, desc);
|
||
|
|
||
|
if (statVerify(pStatNextSlot))
|
||
|
{
|
||
|
((sUserIdStatData*)pStatSlot)->m_NextUserId = static_cast<sUserIdStatData*>(pStatNextSlot);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//Silently fail to add the stat if already exists - due to 360/ps3 cRc's this can happen.
|
||
|
if (statVerify(newStatPtr.GetKey().IsValid()) && !IsKeyValid(newStatPtr.GetKey()))
|
||
|
{
|
||
|
result = true;
|
||
|
|
||
|
m_aStatsData.Insert(newStatPtr);
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (pStatNextSlot)
|
||
|
{
|
||
|
char szName[MAX_STAT_LABEL_SIZE];
|
||
|
snprintf(szName, MAX_STAT_LABEL_SIZE, "%s_NEXT", name);
|
||
|
sStatDataPtr nextStatPtr(pStatNextSlot);
|
||
|
nextStatPtr.SetKey(szName);
|
||
|
m_aStatsData.Insert(nextStatPtr);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pStatSlot)
|
||
|
{
|
||
|
delete pStatSlot;
|
||
|
}
|
||
|
|
||
|
#if RLGAMERHANDLE_NP
|
||
|
if (pStatNextSlot)
|
||
|
{
|
||
|
delete pStatNextSlot;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::RegisterNewStatForEachCharacter(sStatParser& statparser, const bool singlePlayerFile)
|
||
|
{
|
||
|
char szName[MAX_STAT_LABEL_SIZE];
|
||
|
ConstString statName = statparser.m_Name;
|
||
|
|
||
|
// Check that the stat has correct size. Otherwise it trims its name silently, screwing anything relying on that stat
|
||
|
statAssertf(strlen(statparser.m_Name.c_str()) + 4 < MAX_STAT_LABEL_SIZE, "Stat name %s is too long, please shorten it by %" I64FMT "d chars", statparser.m_Name.c_str(), strlen(statparser.m_Name.c_str()) + 4 - MAX_STAT_LABEL_SIZE + 1);
|
||
|
|
||
|
if (singlePlayerFile)
|
||
|
{
|
||
|
for (int i = 0; i < STAT_SP_CATEGORY_CHAR2; i++)
|
||
|
{
|
||
|
formatf(szName, MAX_STAT_LABEL_SIZE, "SP%d_%s", i, statName.c_str());
|
||
|
statparser.m_Name = szName;
|
||
|
|
||
|
//Set the group if it isn't already
|
||
|
if (!statparser.m_GroupIsSet)
|
||
|
{
|
||
|
statparser.m_Desc.SetGroup(static_cast<eProfileStatsGroups>(PS_GRP_SP_1 + i));
|
||
|
}
|
||
|
|
||
|
RegisterNewStat(statparser);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
u32 category = statparser.m_Desc.GetCategory();
|
||
|
|
||
|
for (int i=0; i<MAX_NUM_MP_CHARS; i++)
|
||
|
{
|
||
|
formatf(szName, MAX_STAT_LABEL_SIZE, "MP%d_%s", i, statName.c_str());
|
||
|
statparser.m_Name = szName;
|
||
|
|
||
|
//Set Savegame Category - sets the index of the savegame file for this stat.
|
||
|
if (category > 0)
|
||
|
{
|
||
|
statparser.m_Desc.SetCategory(category+i);
|
||
|
}
|
||
|
|
||
|
//Set the group if it isn't already
|
||
|
if (!statparser.m_GroupIsSet)
|
||
|
{
|
||
|
statparser.m_Desc.SetGroup(static_cast<eProfileStatsGroups>(PS_GRP_MP_1 + i));
|
||
|
}
|
||
|
|
||
|
RegisterNewStat(statparser);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SpewStats()
|
||
|
{
|
||
|
#if !__FINAL
|
||
|
statDebugf3("-----------------------------------------------------------------");
|
||
|
statDebugf3("Stats: ");
|
||
|
statDebugf3("-----------------------------------------------------------------");
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while ((iter != m_aStatsData.End()))
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (statVerify(pStat))
|
||
|
{
|
||
|
statDebugf3("Stat - Id=\"%s:%d\"", iter.GetKeyName(), iter.GetKey().GetHash());
|
||
|
pStat->ToString();
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
|
||
|
statDebugf3("-----------------------------------------------------------------");
|
||
|
#endif // !__FINAL
|
||
|
}
|
||
|
|
||
|
CStatsDataMgr::StatsMap::Iterator
|
||
|
CStatsDataMgr::StatIterBegin()
|
||
|
{
|
||
|
return m_aStatsData.Begin();
|
||
|
}
|
||
|
|
||
|
CStatsDataMgr::StatsMap::Iterator
|
||
|
CStatsDataMgr::StatIterEnd()
|
||
|
{
|
||
|
return m_aStatsData.End();
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CStatsDataMgr::StatIterFind(const StatId& keyHash, CStatsDataMgr::StatsMap::Iterator& iter)
|
||
|
{
|
||
|
return m_aStatsData.GetIterator(keyHash, iter);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::StartNetworkMatch()
|
||
|
{
|
||
|
m_CallbackEndReadRemoteProfileStats.Bind(this, &CStatsDataMgr::EndReadRemoteProfileStatsCallback);
|
||
|
|
||
|
statAssert(0 == m_NumHighPriorityStats);
|
||
|
statAssert(!m_HighPriorityStatIds);
|
||
|
statAssert(0 == m_NumLowPriorityStats);
|
||
|
statAssert(!m_LowPriorityStatIds);
|
||
|
|
||
|
EndNetworkMatch();
|
||
|
|
||
|
statDebugf1("Start Match");
|
||
|
|
||
|
CStatsDataMgr::StatsMap::Iterator iter = StatIterBegin();
|
||
|
while (iter != StatIterEnd())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (statVerify(pStat) && pStat->GetDesc().GetReadForRemotePlayers())
|
||
|
{
|
||
|
statAssert(pStat->GetDesc().GetIsOnlineData());
|
||
|
statAssert(pStat->GetDesc().GetIsProfileStat());
|
||
|
|
||
|
if (m_NumHighPriorityStats+m_NumLowPriorityStats < MAX_NUM_READ_REMOTE_GAMER_STATS)
|
||
|
{
|
||
|
if (PS_HIGH_PRIORITY == pStat->GetDesc().GetDownloadPriority())
|
||
|
{
|
||
|
m_NumHighPriorityStats++;
|
||
|
}
|
||
|
else if (statVerify(PS_LOW_PRIORITY == pStat->GetDesc().GetDownloadPriority()))
|
||
|
{
|
||
|
m_NumLowPriorityStats++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
|
||
|
statDebugf1("...... Total High priority stats=%d", m_NumHighPriorityStats);
|
||
|
if (0 < m_NumHighPriorityStats)
|
||
|
{
|
||
|
m_HighPriorityStatIds = rage_new int[m_NumHighPriorityStats];
|
||
|
}
|
||
|
|
||
|
statDebugf1("...... Total Low priority stats=%d", m_NumLowPriorityStats);
|
||
|
if (0 < m_NumLowPriorityStats)
|
||
|
{
|
||
|
m_LowPriorityStatIds = rage_new int[m_NumLowPriorityStats];
|
||
|
}
|
||
|
|
||
|
unsigned indexHigh = 0;
|
||
|
unsigned indexLow = 0;
|
||
|
iter = StatIterBegin();
|
||
|
while (iter != StatIterEnd())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (statVerify(pStat) && pStat->GetDesc().GetReadForRemotePlayers())
|
||
|
{
|
||
|
statAssert(pStat->GetDesc().GetIsOnlineData());
|
||
|
statAssert(pStat->GetDesc().GetIsProfileStat());
|
||
|
|
||
|
if (indexHigh+indexLow < MAX_NUM_READ_REMOTE_GAMER_STATS)
|
||
|
{
|
||
|
if (PS_HIGH_PRIORITY == pStat->GetDesc().GetDownloadPriority())
|
||
|
{
|
||
|
statDebugf1("...... Add HIGH_PRIORITY stat=\"%s\" id=%s", iter.GetKeyName(), iter.GetKeyName());
|
||
|
m_HighPriorityStatIds[indexHigh] = iter.GetKey();
|
||
|
++indexHigh;
|
||
|
}
|
||
|
else if (statVerify(PS_LOW_PRIORITY == pStat->GetDesc().GetDownloadPriority()))
|
||
|
{
|
||
|
statDebugf1("...... Add LOW_PRIORITY stat=\"%s\" id=%s", iter.GetKeyName(), iter.GetKeyName());
|
||
|
m_LowPriorityStatIds[indexLow] = iter.GetKey();
|
||
|
++indexLow;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
statErrorf("...... Stat=\"%s\" is ignored from the profile stats that can be downloaded for remote players.", iter.GetKeyName());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
|
||
|
//Setup profile stats for remote players
|
||
|
for (int i=0; i<MAX_NUM_REMOTE_GAMERS; i++)
|
||
|
{
|
||
|
rlProfileStatsValue* highrecords = (0 < m_NumHighPriorityStats) ? rage_new rlProfileStatsValue[m_NumHighPriorityStats] : 0;
|
||
|
m_HighPriorityRecords[i].ResetRecords(m_NumHighPriorityStats, highrecords);
|
||
|
rlProfileStatsValue* lowrecords = (0 < m_NumLowPriorityStats) ? rage_new rlProfileStatsValue[m_NumLowPriorityStats] : 0;
|
||
|
m_LowPriorityRecords[i].ResetRecords(m_NumLowPriorityStats, lowrecords);
|
||
|
}
|
||
|
|
||
|
m_ReadSessionProfileStats.Init(m_NumHighPriorityStats
|
||
|
,m_HighPriorityStatIds
|
||
|
,m_HighPriorityRecords
|
||
|
,m_NumLowPriorityStats
|
||
|
,m_LowPriorityStatIds
|
||
|
,m_LowPriorityRecords
|
||
|
,MAX_NUM_REMOTE_GAMERS
|
||
|
,m_CallbackEndReadRemoteProfileStats);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::EndNetworkMatch()
|
||
|
{
|
||
|
m_NumHighPriorityStats = 0;
|
||
|
if (m_HighPriorityStatIds)
|
||
|
{
|
||
|
delete [] m_HighPriorityStatIds;
|
||
|
}
|
||
|
|
||
|
m_NumLowPriorityStats = 0;
|
||
|
if (m_LowPriorityStatIds)
|
||
|
{
|
||
|
delete [] m_LowPriorityStatIds;
|
||
|
}
|
||
|
|
||
|
for (int i=0; i<MAX_NUM_REMOTE_GAMERS; i++)
|
||
|
{
|
||
|
m_HighPriorityRecords[i].ResetRecords(0, 0);
|
||
|
m_LowPriorityRecords[i].ResetRecords(0, 0);
|
||
|
}
|
||
|
|
||
|
m_ReadSessionProfileStats.Shutdown();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::PlayerHasJoinedSession(const ActivePlayerIndex playerIndex)
|
||
|
{
|
||
|
m_ReadSessionProfileStats.PlayerHasJoined(playerIndex);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::PlayerHasLeftSession(const ActivePlayerIndex playerIndex)
|
||
|
{
|
||
|
m_ReadSessionProfileStats.PlayerHasLeft(playerIndex);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::EndReadRemoteProfileStatsCallback(const bool /*succeeded*/)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::SyncRemoteGamerProfileStats(const unsigned priority)
|
||
|
{
|
||
|
if (!m_ReadSessionProfileStats.Pending())
|
||
|
{
|
||
|
m_ReadSessionProfileStats.ReadStats(priority);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const rlProfileStatsValue*
|
||
|
CStatsDataMgr::GetRemoteProfileStat(const rlGamerHandle& handle, const int statId) const
|
||
|
{
|
||
|
if (!NetworkInterface::IsGameInProgress())
|
||
|
return NULL;
|
||
|
|
||
|
bool statFound = false;
|
||
|
unsigned valueColumn = 0;
|
||
|
const CProfileStatsRecords* records = NULL;
|
||
|
|
||
|
for (unsigned i=0; i<m_NumHighPriorityStats && !statFound; i++)
|
||
|
{
|
||
|
if (statId == m_HighPriorityStatIds[i])
|
||
|
{
|
||
|
records = &m_HighPriorityRecords[0];
|
||
|
valueColumn = i;
|
||
|
statFound = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (unsigned i=0; i<m_NumLowPriorityStats && !statFound; i++)
|
||
|
{
|
||
|
if (statId == m_LowPriorityStatIds[i])
|
||
|
{
|
||
|
records = &m_LowPriorityRecords[0];
|
||
|
valueColumn = i;
|
||
|
statFound = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (records)
|
||
|
{
|
||
|
for (int i=0; i<MAX_NUM_REMOTE_GAMERS; i++)
|
||
|
{
|
||
|
if (handle == records[i].GetGamerHandle() && statVerify(valueColumn < records[i].GetNumValues() && records[i].GetValues()))
|
||
|
{
|
||
|
return (&records[i].GetValue(valueColumn));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#if __BANK
|
||
|
|
||
|
static int s_BankPriority = 1;
|
||
|
static int s_Bankcharacter = 1;
|
||
|
static int s_BankMpSlot = 0;
|
||
|
static bool s_ToggleScriptChangesToTerritoryData = false;
|
||
|
static int s_BankProfileStatGroup = 0;
|
||
|
static int s_BankProfileStatsToDirty = 0;
|
||
|
|
||
|
static bkGroup* s_rGroup_af;
|
||
|
static bkGroup* s_rGroup_gl;
|
||
|
static bkGroup* s_rGroup_mr;
|
||
|
static bkGroup* s_rGroup_sw;
|
||
|
static bkGroup* s_rGroup_xz;
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_CreateWidgets()
|
||
|
{
|
||
|
Assertf(GetStatObfuscationDisabled(),"You're creating stats widgets, but profile stats are obfuscated in the memory, use -disableStatObfuscation in your commandline if you wish to modify stats");
|
||
|
bkBank* bank = BANKMGR.FindBank("Stats");
|
||
|
if (bank)
|
||
|
{
|
||
|
bank->GetChild()->Destroy();
|
||
|
|
||
|
bank->AddSeparator();
|
||
|
|
||
|
char title[256];
|
||
|
formatf(title, COUNTOF(title), "Data sizes total=\"%u\", profile=\"%d\".", Bank_CalculateAllDataSizes(), Bank_CalculateAllDataSizes(true));
|
||
|
bank->AddTitle(title);
|
||
|
|
||
|
bank->AddSeparator();
|
||
|
|
||
|
m_SaveMgr.Bank_InitWidgets( *bank );
|
||
|
|
||
|
bank->PushGroup("Online", false);
|
||
|
{
|
||
|
bank->PushGroup("Territories", false);
|
||
|
{
|
||
|
bank->AddButton("Clear all Territory data", datCallback(MFA(CStatsDataMgr::Bank_ClearAllTerritoryData), (datBase*)this));
|
||
|
bank->AddToggle("Debug Changes to Territory data from script", &s_ToggleScriptChangesToTerritoryData, datCallback(MFA(CStatsDataMgr::Bank_ToggleScriptChangesToTerritoryData), (datBase*)this));
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->AddSlider("Clear character", &s_Bankcharacter, 0, 4, 1);
|
||
|
bank->AddButton("Clear all online character", datCallback(MFA(CStatsDataMgr::Bank_ResetAllOnlineCharacterStats), (datBase*)this));
|
||
|
|
||
|
bank->AddSeparator();
|
||
|
bank->AddSlider("Profile stats Group", &s_BankProfileStatGroup, 0, 255, 1);
|
||
|
bank->AddButton("Clear Profile stats Group", datCallback(MFA(CStatsDataMgr::Bank_ResetAllProfileStatsByGroup), (datBase*)this));
|
||
|
|
||
|
bank->AddSeparator();
|
||
|
bank->AddToggle("Enable Continuous Peer Reports", &CContextMenuHelper::m_bNonStopReports);
|
||
|
|
||
|
bank->AddSeparator();
|
||
|
bank->AddButton("Force Synchronize Profile stats", datCallback(MFA(CStatsDataMgr::Bank_ForceSynchProfileStats), (datBase*)this));
|
||
|
bank->AddButton("Run Profile Sync Consistency Check", datCallback(MFA(CStatsDataMgr::Bank_CheckProfileSyncConsistency), (datBase*)this));
|
||
|
bank->AddButton("Flush Profile stats", datCallback(MFA(CStatsDataMgr::Bank_FlushProfileStats), (datBase*)this));
|
||
|
bank->AddButton("Force Flush Profile stats", datCallback(MFA(CStatsDataMgr::Bank_ForceFlushProfileStats), (datBase*)this));
|
||
|
bank->AddSlider("Command Value - Dirty some profile stats", &s_BankProfileStatsToDirty, 0, 6000, 1);
|
||
|
bank->AddButton("Dirty some profile stats", datCallback(MFA(CStatsDataMgr::Bank_DirtySomeProfileStats), (datBase*)this));
|
||
|
bank->AddSeparator();
|
||
|
bank->AddSlider("Update Priority", &s_BankPriority, 0, 2, 1);
|
||
|
bank->AddButton("Update players Profile stats", datCallback(MFA(CStatsDataMgr::Bank_ToggleUpdateProfileStats), (datBase*)this));
|
||
|
bank->AddSeparator();
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("Debug Changes", false);
|
||
|
{
|
||
|
bank->AddToggle("Script changes to Stats", &m_ScriptSpew);
|
||
|
bank->AddButton("Make GET commands Fail", datCallback(MFA(CStatsDataMgr::Bank_ToggleTestStatGetFail), (datBase*)this));
|
||
|
bank->AddButton("TestCommandLeaderboards2ReadRankPrediction", datCallback(MFA(CStatsDataMgr::Bank_TestCommandLeaderboards2ReadRankPrediction), (datBase*)this));
|
||
|
bank->AddButton("Enable/Disable Stats tracking", datCallback(MFA(CStatsDataMgr::Bank_Enable_Disable_Stats_Tracking), (datBase*)this));
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->AddSeparator();
|
||
|
|
||
|
bank->AddButton("Create Single Player Stats", datCallback(MFA(CStatsDataMgr::Bank_CreateSpWidgets), (datBase*)this));
|
||
|
bank->AddSeparator();
|
||
|
bank->AddSlider("Multiplayer character for mp stats", &s_BankMpSlot, 0, 4, 1);
|
||
|
bank->AddButton("Create Multiplayer Player Stats", datCallback(MFA(CStatsDataMgr::Bank_CreateMpWidgets), (datBase*)this));
|
||
|
bank->AddButton("Create MP Stats using Current Slot", datCallback(MFA(CStatsDataMgr::Bank_CreateMpWidgetsCurrentSlot), (datBase*)this));
|
||
|
bank->AddButton("Display selected stats on screen", datCallback(MFA(CStatsDataMgr::GrcDebugDraw), (datBase*)this));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_InitWidgets()
|
||
|
{
|
||
|
m_ScriptSpew = false;
|
||
|
m_TestFail = false;
|
||
|
|
||
|
m_bSpBankCreated = false;
|
||
|
m_bMpBankCreated = false;
|
||
|
|
||
|
bkBank* bank = BANKMGR.FindBank("Stats");
|
||
|
if (!bank)
|
||
|
{
|
||
|
bank = &BANKMGR.CreateBank("Stats");
|
||
|
bank->AddButton("Create stats widgets", datCallback(MFA(CStatsDataMgr::Bank_CreateWidgets), (datBase*)this));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CStatsDataMgr::Bank_CreateSpWidgets()
|
||
|
{
|
||
|
m_TestFail = false;
|
||
|
|
||
|
bkBank* bank = BANKMGR.FindBank("Stats");
|
||
|
|
||
|
if (bank && m_BkGroup)
|
||
|
{
|
||
|
bank->DeleteGroup(*m_BkGroup);
|
||
|
m_BkGroup = 0;
|
||
|
m_bSpBankCreated = false;
|
||
|
}
|
||
|
|
||
|
if ((!m_bSpBankCreated) && bank && !m_BkGroup)
|
||
|
{
|
||
|
m_bSpBankCreated = true;
|
||
|
|
||
|
m_BkGroup = bank->PushGroup("Data Manager", false);
|
||
|
{
|
||
|
char namecmp0[MAX_STAT_LABEL_SIZE];
|
||
|
char namecmp1[MAX_STAT_LABEL_SIZE];
|
||
|
|
||
|
//Single Player Stats
|
||
|
bank->PushGroup("SinglePlayer", false);
|
||
|
{
|
||
|
bank->PushGroup("System", false);
|
||
|
Bank_AddStatsToGroup(bank, "_");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("SP0", false);
|
||
|
Bank_AddStatsToGroup(bank, "SP0");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("SP1", false);
|
||
|
Bank_AddStatsToGroup(bank, "SP1");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("SP2", false);
|
||
|
Bank_AddStatsToGroup(bank, "SP2");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("APP", false);
|
||
|
Bank_AddStatsToGroup(bank, "APP");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("SM", false);
|
||
|
Bank_AddStatsToGroup(bank, "SM");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("MS", false);
|
||
|
Bank_AddStatsToGroup(bank, "MS");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("Other", false);
|
||
|
{
|
||
|
StatsMap::Iterator iter = CStatsMgr::GetStatsDataMgr().StatIterBegin();
|
||
|
while ((iter != CStatsMgr::GetStatsDataMgr().StatIterEnd()))
|
||
|
{
|
||
|
sStatDataPtr* ppStat = *iter;
|
||
|
sStatData* pStat = ppStat && ppStat->KeyIsvalid() ? *ppStat : 0;
|
||
|
|
||
|
if (pStat && !pStat->GetDesc().GetIsOnlineData())
|
||
|
{
|
||
|
sysMemSet(namecmp0, 0, MAX_STAT_LABEL_SIZE);
|
||
|
formatf(namecmp0, 4, iter.GetKeyName());
|
||
|
|
||
|
sysMemSet(namecmp1, 0, MAX_STAT_LABEL_SIZE);
|
||
|
formatf(namecmp1, 3, iter.GetKeyName());
|
||
|
|
||
|
if ('_' != namecmp0[0]
|
||
|
&& (stricmp(namecmp0, "SP0") != 0)
|
||
|
&& (stricmp(namecmp0, "SP1") != 0)
|
||
|
&& (stricmp(namecmp0, "SP2") != 0)
|
||
|
&& (stricmp(namecmp0, "APP") != 0)
|
||
|
&& (stricmp(namecmp1, "SM") != 0)
|
||
|
&& (stricmp(namecmp1, "MS") != 0))
|
||
|
{
|
||
|
bank->PushGroup(iter.GetKeyName(), false);
|
||
|
{
|
||
|
bank->AddToggle("Debug Changes", &pStat->m_ShowChanges, NullCB, NULL);
|
||
|
bank->AddToggle("Show on screen", &pStat->m_ShowOnScreen, datCallback(MFA1(CStatsDataMgr::AddToDebugDraw), (datBase*)this, (CallbackData)pStat));
|
||
|
Bank_AddStatWidgetValue(bank, ppStat);
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CStatsDataMgr::Bank_CreateMpWidgetsCurrentSlot()
|
||
|
{
|
||
|
s_BankMpSlot = StatsInterface::GetCurrentMultiplayerCharaterSlot();
|
||
|
Bank_CreateMpWidgets();
|
||
|
}
|
||
|
|
||
|
void CStatsDataMgr::Bank_CreateMpWidgets()
|
||
|
{
|
||
|
m_TestFail = false;
|
||
|
|
||
|
bkBank* bank = BANKMGR.FindBank("Stats");
|
||
|
|
||
|
if (bank && m_BkGroup)
|
||
|
{
|
||
|
bank->DeleteGroup(*m_BkGroup);
|
||
|
m_BkGroup = 0;
|
||
|
m_bMpBankCreated = false;
|
||
|
}
|
||
|
|
||
|
if ((!m_bMpBankCreated) && bank && !m_BkGroup)
|
||
|
{
|
||
|
s_rGroup_af = NULL;
|
||
|
s_rGroup_gl = NULL;
|
||
|
s_rGroup_mr = NULL;
|
||
|
s_rGroup_sw = NULL;
|
||
|
s_rGroup_xz = NULL;
|
||
|
|
||
|
m_bMpBankCreated = true;
|
||
|
|
||
|
m_BkGroup = bank->PushGroup("Data Manager", false);
|
||
|
{
|
||
|
char namecmp0[MAX_STAT_LABEL_SIZE];
|
||
|
char namecmp1[MAX_STAT_LABEL_SIZE];
|
||
|
|
||
|
//Multiplayer Player Stats
|
||
|
bank->PushGroup("Multiplayer", false);
|
||
|
{
|
||
|
if (0 == s_BankMpSlot)
|
||
|
{
|
||
|
bank->PushGroup("MP0", false);
|
||
|
s_rGroup_af = bank->PushGroup("a-f"); bank->PopGroup();
|
||
|
s_rGroup_gl = bank->PushGroup("g-l"); bank->PopGroup();
|
||
|
s_rGroup_mr = bank->PushGroup("m-r"); bank->PopGroup();
|
||
|
s_rGroup_sw = bank->PushGroup("s-w"); bank->PopGroup();
|
||
|
s_rGroup_xz = bank->PushGroup("x-z"); bank->PopGroup();
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
else if (1 == s_BankMpSlot)
|
||
|
{
|
||
|
bank->PushGroup("MP1", false);
|
||
|
s_rGroup_af = bank->PushGroup("a-f"); bank->PopGroup();
|
||
|
s_rGroup_gl = bank->PushGroup("g-l"); bank->PopGroup();
|
||
|
s_rGroup_mr = bank->PushGroup("m-r"); bank->PopGroup();
|
||
|
s_rGroup_sw = bank->PushGroup("s-w"); bank->PopGroup();
|
||
|
s_rGroup_xz = bank->PushGroup("x-z"); bank->PopGroup();
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
else if (2 == s_BankMpSlot)
|
||
|
{
|
||
|
bank->PushGroup("MP2", false);
|
||
|
s_rGroup_af = bank->PushGroup("a-f"); bank->PopGroup();
|
||
|
s_rGroup_gl = bank->PushGroup("g-l"); bank->PopGroup();
|
||
|
s_rGroup_mr = bank->PushGroup("m-r"); bank->PopGroup();
|
||
|
s_rGroup_sw = bank->PushGroup("s-w"); bank->PopGroup();
|
||
|
s_rGroup_xz = bank->PushGroup("x-z"); bank->PopGroup();
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
else if (3 == s_BankMpSlot)
|
||
|
{
|
||
|
bank->PushGroup("MP3", false);
|
||
|
s_rGroup_af = bank->PushGroup("a-f"); bank->PopGroup();
|
||
|
s_rGroup_gl = bank->PushGroup("g-l"); bank->PopGroup();
|
||
|
s_rGroup_mr = bank->PushGroup("m-r"); bank->PopGroup();
|
||
|
s_rGroup_sw = bank->PushGroup("s-w"); bank->PopGroup();
|
||
|
s_rGroup_xz = bank->PushGroup("x-z"); bank->PopGroup();
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
else if (4 == s_BankMpSlot)
|
||
|
{
|
||
|
bank->PushGroup("MP4", false);
|
||
|
s_rGroup_af = bank->PushGroup("a-f"); bank->PopGroup();
|
||
|
s_rGroup_gl = bank->PushGroup("g-l"); bank->PopGroup();
|
||
|
s_rGroup_mr = bank->PushGroup("m-r"); bank->PopGroup();
|
||
|
s_rGroup_sw = bank->PushGroup("s-w"); bank->PopGroup();
|
||
|
s_rGroup_xz = bank->PushGroup("x-z"); bank->PopGroup();
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
|
||
|
bank->PushGroup("MPPLY", false);
|
||
|
Bank_AddStatsToGroup(bank, "MPPLY");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("MPGEN", false);
|
||
|
Bank_AddStatsToGroup(bank, "MPGEN");
|
||
|
bank->PopGroup();
|
||
|
|
||
|
bank->PushGroup("Other", false);
|
||
|
{
|
||
|
StatsMap::Iterator iter = CStatsMgr::GetStatsDataMgr().StatIterBegin();
|
||
|
while ((iter != CStatsMgr::GetStatsDataMgr().StatIterEnd()))
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat && pStat->GetDesc().GetIsOnlineData())
|
||
|
{
|
||
|
sysMemSet(namecmp0, 0, MAX_STAT_LABEL_SIZE);
|
||
|
formatf(namecmp0, 4, iter.GetKeyName());
|
||
|
|
||
|
sysMemSet(namecmp1, 0, MAX_STAT_LABEL_SIZE);
|
||
|
formatf(namecmp1, 6, iter.GetKeyName());
|
||
|
|
||
|
if ((stricmp(namecmp0, "MP0") != 0)
|
||
|
&& (stricmp(namecmp0, "MP1") != 0)
|
||
|
&& (stricmp(namecmp0, "MP2") != 0)
|
||
|
&& (stricmp(namecmp0, "MP3") != 0)
|
||
|
&& (stricmp(namecmp0, "MP4") != 0)
|
||
|
&& (stricmp(namecmp1, "MPPLY") != 0)
|
||
|
&& (stricmp(namecmp1, "MPGEN") != 0))
|
||
|
{
|
||
|
bank->PushGroup(iter.GetKeyName(), false);
|
||
|
{
|
||
|
bank->AddToggle("Debug Changes", &pStat->m_ShowChanges, NullCB, NULL);
|
||
|
bank->AddToggle("Show on screen", &pStat->m_ShowOnScreen, datCallback(MFA1(CStatsDataMgr::AddToDebugDraw), (datBase*)this, (CallbackData)pStat));
|
||
|
Bank_AddStatWidgetValue(bank, *iter);
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
|
||
|
if (statVerify(s_rGroup_af)
|
||
|
&& statVerify(s_rGroup_gl)
|
||
|
&& statVerify(s_rGroup_mr)
|
||
|
&& statVerify(s_rGroup_sw)
|
||
|
&& statVerify(s_rGroup_xz))
|
||
|
{
|
||
|
if (0 == s_BankMpSlot)
|
||
|
Bank_AddStatsToGroupMP(bank, "MP0");
|
||
|
else if (1 == s_BankMpSlot)
|
||
|
Bank_AddStatsToGroupMP(bank, "MP1");
|
||
|
else if (2 == s_BankMpSlot)
|
||
|
Bank_AddStatsToGroupMP(bank, "MP2");
|
||
|
else if (3 == s_BankMpSlot)
|
||
|
Bank_AddStatsToGroupMP(bank, "MP3");
|
||
|
else if (4 == s_BankMpSlot)
|
||
|
Bank_AddStatsToGroupMP(bank, "MP4");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_AddStatWidgetValue(bkBank* bank, sStatDataPtr* ppStat)
|
||
|
{
|
||
|
sStatData* pStat = ppStat ? *ppStat : 0;
|
||
|
|
||
|
if (bank && pStat && !pStat->IsObfuscated())
|
||
|
{
|
||
|
if (0 < pStat->GetType() && pStat->GetType() < NUMBER_OF_STATS_TYPE_IDS)
|
||
|
{
|
||
|
char title[256];
|
||
|
formatf(title, COUNTOF(title), "Type \"%s\"", sStatsTypesNames[pStat->GetType()].m_Name);
|
||
|
bank->AddTitle(title);
|
||
|
}
|
||
|
|
||
|
if (pStat->GetIsBaseType(STAT_TYPE_INT))
|
||
|
{
|
||
|
bank->AddSlider("Value", &((sIntStatData*)pStat)->m_Value, MIN_INT32, MAX_INT32, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_TEXTLABEL))
|
||
|
{
|
||
|
bank->AddText("Value", ((sLabelStatData*)pStat)->m_Label, MAX_STAT_LABEL_SIZE, true, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_STRING))
|
||
|
{
|
||
|
bank->AddText("Value", ((sStringStatData*)pStat)->m_Value, MAX_STAT_STRING_SIZE, true, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_FLOAT))
|
||
|
{
|
||
|
bank->AddSlider("Value", &((sFloatStatData*)pStat)->m_Value, -FLT_MAX / 1000.0f, +FLT_MAX / 1000.0f, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_BOOLEAN))
|
||
|
{
|
||
|
bank->AddToggle("Value", &((sBoolStatData*)pStat)->m_Value, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_UINT8))
|
||
|
{
|
||
|
bank->AddSlider("Value", &((sUns8StatData*)pStat)->m_Value, 0, MAX_UINT8, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_UINT16))
|
||
|
{
|
||
|
bank->AddSlider("Value", &((sUns16StatData*)pStat)->m_Value, 0, MAX_UINT16, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_UINT32))
|
||
|
{
|
||
|
bank->AddSlider("Value", &((sUns32StatData*)pStat)->m_Value, 0, MAX_UINT32, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
if (pStat->GetIsBaseType(STAT_TYPE_INT64))
|
||
|
{
|
||
|
bank->AddSlider("Value", (int*)&((sInt64StatData*)pStat)->m_Value, MIN_INT32, MAX_INT32, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_DATE))
|
||
|
{
|
||
|
bank->AddSlider("Year", &((sDateStatData*)pStat)->m_Date.m_year, MIN_INT32, MAX_INT32, 0, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Month", &((sDateStatData*)pStat)->m_Date.m_month, MIN_INT32, MAX_UINT32, 0, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Day", &((sDateStatData*)pStat)->m_Date.m_day, MIN_INT32, MAX_UINT32, 0, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Hour", &((sDateStatData*)pStat)->m_Date.m_hour, MIN_INT32, MAX_UINT32, 0, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Minutes", &((sDateStatData*)pStat)->m_Date.m_minutes, MIN_INT32, MAX_UINT32, 0, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Seconds", &((sDateStatData*)pStat)->m_Date.m_seconds, MIN_INT32, MAX_UINT32, 0, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Milliseconds", &((sDateStatData*)pStat)->m_Date.m_milliseconds, MIN_INT32, MAX_UINT32, 0, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_POS))
|
||
|
{
|
||
|
bank->AddVector("Value", &((sPosStatData*)pStat)->m_Position, -FLT_MAX / 1000.0f, +FLT_MAX / 1000.0f, 0.0f, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_UINT64))
|
||
|
{
|
||
|
bank->AddSlider("Value Lo", &((sUns64StatData*)pStat)->m_ValueLo, 0, MAX_UINT32, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Value Hi", &((sUns64StatData*)pStat)->m_ValueHi, 0, MAX_UINT32, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_PACKED))
|
||
|
{
|
||
|
bank->AddText("Value", ((sPackedStatData*)pStat)->m_WidgetValue, sPackedStatData::WIDGET_STRING_SIZE, true, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
bank->AddSlider("Number Of Bits", &((sPackedStatData*)pStat)->m_NumberOfBits, MIN_INT32, MAX_INT32, 1, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
else if (pStat->GetIsBaseType(STAT_TYPE_USERID))
|
||
|
{
|
||
|
bank->AddText("Value", ((sUserIdStatData*)pStat)->m_WidgetValue, sUserIdStatData::WIDGET_STRING_SIZE, true, datCallback(MFA1(CStatsDataMgr::Bank_SetStatDirty), (datBase*)this, (CallbackData)ppStat));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_AddStatToHisGroup(bkBank* bank, StatsMap::Iterator& iter, sStatData* pStat)
|
||
|
{
|
||
|
if (statVerify(pStat) && statVerify(pStat))
|
||
|
{
|
||
|
const char* name = iter.GetKeyName();
|
||
|
char ch = name[4];
|
||
|
|
||
|
if( (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f') )
|
||
|
bank->SetCurrentGroup( *s_rGroup_af );
|
||
|
else if( (ch >= 'G' && ch <= 'L') || (ch >= 'g' && ch <= 'l') )
|
||
|
bank->SetCurrentGroup( *s_rGroup_gl );
|
||
|
else if( (ch >= 'M' && ch <= 'R') || (ch >= 'm' && ch <= 'r') )
|
||
|
bank->SetCurrentGroup( *s_rGroup_mr );
|
||
|
else if( (ch >= 'S' && ch <= 'W') || (ch >= 's' && ch <= 'w') )
|
||
|
bank->SetCurrentGroup( *s_rGroup_sw );
|
||
|
else
|
||
|
bank->SetCurrentGroup( *s_rGroup_xz );
|
||
|
|
||
|
{
|
||
|
bank->PushGroup(iter.GetKeyName(), false);
|
||
|
{
|
||
|
bank->AddToggle("Debug Changes", &pStat->m_ShowChanges, NullCB, NULL);
|
||
|
bank->AddToggle("Show on screen", &pStat->m_ShowOnScreen, datCallback(MFA1(CStatsDataMgr::AddToDebugDraw), (datBase*)this, (CallbackData)pStat));
|
||
|
Bank_AddStatWidgetValue(bank, *iter);
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
|
||
|
if( (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f') )
|
||
|
bank->UnSetCurrentGroup( *s_rGroup_af );
|
||
|
else if( (ch >= 'G' && ch <= 'L') || (ch >= 'g' && ch <= 'l') )
|
||
|
bank->UnSetCurrentGroup( *s_rGroup_gl );
|
||
|
else if( (ch >= 'M' && ch <= 'R') || (ch >= 'm' && ch <= 'r') )
|
||
|
bank->UnSetCurrentGroup( *s_rGroup_mr );
|
||
|
else if( (ch >= 'S' && ch <= 'W') || (ch >= 's' && ch <= 'w') )
|
||
|
bank->UnSetCurrentGroup( *s_rGroup_sw );
|
||
|
else
|
||
|
bank->UnSetCurrentGroup( *s_rGroup_xz );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_AddStatsToGroup(bkBank* bank, const char* name)
|
||
|
{
|
||
|
if (statVerifyf(bank, "Invalid Bank Specified!") && statVerifyf(name, "Invalid stat name specified!"))
|
||
|
{
|
||
|
unsigned sizeofname = ustrlen(name);
|
||
|
sizeofname++;
|
||
|
|
||
|
char namecmp[MAX_STAT_LABEL_SIZE];
|
||
|
|
||
|
StatsMap::Iterator iter = CStatsMgr::GetStatsDataMgr().StatIterBegin();
|
||
|
while ((iter != CStatsMgr::GetStatsDataMgr().StatIterEnd()))
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
sysMemSet(namecmp, 0, MAX_STAT_LABEL_SIZE);
|
||
|
formatf(namecmp, sizeofname, iter.GetKeyName());
|
||
|
|
||
|
if ((stricmp(namecmp, name) == 0))
|
||
|
{
|
||
|
bank->PushGroup(iter.GetKeyName(), false);
|
||
|
{
|
||
|
bank->AddToggle("Debug Changes", &pStat->m_ShowChanges, NullCB, NULL);
|
||
|
bank->AddToggle("Show on screen", &pStat->m_ShowOnScreen, datCallback(MFA1(CStatsDataMgr::AddToDebugDraw), (datBase*)this, (CallbackData)pStat));
|
||
|
Bank_AddStatWidgetValue(bank, *iter);
|
||
|
}
|
||
|
bank->PopGroup();
|
||
|
}
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_AddStatsToGroupMP(bkBank* bank, const char* name)
|
||
|
{
|
||
|
if (statVerifyf(bank, "Invalid Bank Specified!") && statVerifyf(name, "Invalid stat name specified!"))
|
||
|
{
|
||
|
unsigned sizeofname = ustrlen(name);
|
||
|
sizeofname++;
|
||
|
|
||
|
char namecmp[MAX_STAT_LABEL_SIZE];
|
||
|
|
||
|
StatsMap::Iterator iter = CStatsMgr::GetStatsDataMgr().StatIterBegin();
|
||
|
while ((iter != CStatsMgr::GetStatsDataMgr().StatIterEnd()))
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
sysMemSet(namecmp, 0, MAX_STAT_LABEL_SIZE);
|
||
|
formatf(namecmp, sizeofname, iter.GetKeyName());
|
||
|
|
||
|
if ((stricmp(namecmp, name) == 0))
|
||
|
{
|
||
|
Bank_AddStatToHisGroup(bank, iter, pStat);
|
||
|
}
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
void CStatsDataMgr::GrcDebugDraw()
|
||
|
{
|
||
|
m_enableDebugDraw = !m_enableDebugDraw;
|
||
|
}
|
||
|
|
||
|
void CStatsDataMgr::AddToDebugDraw(sStatData* data)
|
||
|
{
|
||
|
|
||
|
StatsMap::Iterator iter = CStatsMgr::GetStatsDataMgr().StatIterBegin();
|
||
|
const char* statName=NULL;
|
||
|
while ((iter != CStatsMgr::GetStatsDataMgr().StatIterEnd()))
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
if(data==pStat)
|
||
|
{
|
||
|
statName = iter.GetKeyName();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
bool add = data->m_ShowOnScreen;
|
||
|
if(add)
|
||
|
{
|
||
|
sStatDisplayData& debugData = m_debugDrawStatsData.Grow();
|
||
|
debugData.Name=statName;
|
||
|
debugData.Stat=data;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sStatDisplayData findData;
|
||
|
findData.Name=statName;
|
||
|
findData.Stat=data;
|
||
|
m_debugDrawStatsData.DeleteMatches(findData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CStatsDataMgr::UpdateDebugDraw()
|
||
|
{
|
||
|
if(m_enableDebugDraw)
|
||
|
{
|
||
|
bool saveDebug = grcDebugDraw::GetDisplayDebugText();
|
||
|
grcDebugDraw::SetDisplayDebugText(TRUE);
|
||
|
Color32 color = Color_white;
|
||
|
Vector2 vTextRenderPos(0.05f,0.05f);
|
||
|
char maininfo[256];
|
||
|
for(int i=0;i<m_debugDrawStatsData.GetCount();i++)
|
||
|
{
|
||
|
char buff[48];
|
||
|
sStatData* stat = m_debugDrawStatsData[i].Stat;
|
||
|
const char* name = m_debugDrawStatsData[i].Name;
|
||
|
vTextRenderPos.y+=0.015f;
|
||
|
formatf(maininfo,"%s[%s] : %s",name, sStatsTypesNames[stat->GetType()].m_Name, stat->ValueToString(buff, 48));
|
||
|
grcDebugDraw::Text(vTextRenderPos,color,maininfo,true,1.2f,1.2f);
|
||
|
}
|
||
|
grcDebugDraw::SetDisplayDebugText( saveDebug );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_WidgetShutdown()
|
||
|
{
|
||
|
bkBank* bank = BANKMGR.FindBank("Stats");
|
||
|
if (bank && m_BkGroup)
|
||
|
{
|
||
|
bank->DeleteGroup(*m_BkGroup);
|
||
|
}
|
||
|
|
||
|
m_BkGroup = 0;
|
||
|
m_TestFail = false;
|
||
|
|
||
|
m_bSpBankCreated = false;
|
||
|
m_bMpBankCreated = false;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_SetStatDirty(sStatDataPtr* ppStat)
|
||
|
{
|
||
|
if (AssertVerify(ppStat))
|
||
|
{
|
||
|
sStatData* pStat = *ppStat;
|
||
|
if (pStat && pStat->GetIsBaseType(STAT_TYPE_UINT64))
|
||
|
{
|
||
|
u64 data = ((sUns64StatData*)pStat)->m_ValueLo + ((sUns64StatData*)pStat)->m_ValueHi;
|
||
|
CStatsDataMgr::SetStatData(ppStat->GetKey(), &data, sizeof(u64));
|
||
|
}
|
||
|
|
||
|
CStatsMgr::GetStatsDataMgr().SetStatDirty(ppStat->GetKey());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_FlushProfileStats()
|
||
|
{
|
||
|
CLiveManager::GetProfileStatsMgr().Flush(false, NetworkInterface::IsNetworkOpen());
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_ForceFlushProfileStats()
|
||
|
{
|
||
|
CLiveManager::GetProfileStatsMgr().Flush(true, NetworkInterface::IsNetworkOpen());
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_ForceSynchProfileStats()
|
||
|
{
|
||
|
StatsInterface::SyncProfileStats(true/*force*/, NetworkInterface::IsGameInProgress()/*multiplayer*/);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_DirtySomeProfileStats()
|
||
|
{
|
||
|
int dirtiedCount = 0;
|
||
|
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (dirtiedCount < s_BankProfileStatsToDirty &&
|
||
|
iter != m_aStatsData.End())
|
||
|
{
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
sStatData *pStat = *iter;
|
||
|
|
||
|
if (pStat &&
|
||
|
pStat->GetDesc().GetIsProfileStat() &&
|
||
|
pStat->GetDesc().GetSynched())
|
||
|
{
|
||
|
pStat->GetDesc().SetDirty(true);
|
||
|
dirtiedCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_ClearAllTerritoryData()
|
||
|
{
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat && pStat->GetDesc().GetIsOnlineData())
|
||
|
{
|
||
|
//We must dirty the profile here so that it knows about the reset
|
||
|
ResetStat(*iter, true/*dirtyProfile*/);
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_ToggleScriptChangesToTerritoryData()
|
||
|
{
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while (iter != m_aStatsData.End())
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat && pStat->GetDesc().GetIsOnlineData())
|
||
|
{
|
||
|
pStat->m_ShowChanges = s_ToggleScriptChangesToTerritoryData;
|
||
|
}
|
||
|
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_ResetAllOnlineCharacterStats()
|
||
|
{
|
||
|
if (statVerify(-1 < s_Bankcharacter && s_Bankcharacter < 5))
|
||
|
{
|
||
|
ResetAllOnlineCharacterStats(s_Bankcharacter, true, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_ResetAllProfileStatsByGroup()
|
||
|
{
|
||
|
if (statVerify(-1 < s_BankProfileStatGroup))
|
||
|
{
|
||
|
CLiveManager::GetProfileStatsMgr().ResetGroup((u32)s_BankProfileStatGroup);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_ToggleUpdateProfileStats()
|
||
|
{
|
||
|
m_ReadSessionProfileStats.ReadStats((unsigned)s_BankPriority);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_TestCommandLeaderboards2ReadRankPrediction()
|
||
|
{
|
||
|
stats_commands::TestCommandLeaderboards2ReadRankPrediction();
|
||
|
}
|
||
|
|
||
|
unsigned
|
||
|
CStatsDataMgr::Bank_CalculateAllDataSizes(const bool returnProfileStats)
|
||
|
{
|
||
|
static unsigned int_stats = 0;
|
||
|
static unsigned obfint_stats = 0;
|
||
|
static unsigned label_stats = 0;
|
||
|
static unsigned float_stats = 0;
|
||
|
static unsigned obffloat_stats = 0;
|
||
|
static unsigned bool_stats = 0;
|
||
|
static unsigned u8_stats = 0;
|
||
|
static unsigned u16_stats = 0;
|
||
|
static unsigned u32_stats = 0;
|
||
|
static unsigned u64_stats = 0;
|
||
|
static unsigned obfu64_stats = 0;
|
||
|
static unsigned pos_stats = 0;
|
||
|
static unsigned date_stats = 0;
|
||
|
static unsigned string_stats = 0;
|
||
|
static unsigned packed_stats = 0;
|
||
|
static unsigned userid_stats = 0;
|
||
|
static unsigned profilesetting_stats = 0;
|
||
|
static unsigned s64_stats = 0;
|
||
|
static unsigned obfs64_stats = 0;
|
||
|
|
||
|
static unsigned profileint_stats = 0;
|
||
|
static unsigned profilefloat_stats = 0;
|
||
|
static unsigned profilebigints_stats = 0;
|
||
|
|
||
|
if (!int_stats)
|
||
|
{
|
||
|
StatsMap::Iterator iter = m_aStatsData.Begin();
|
||
|
while ((iter != m_aStatsData.End()))
|
||
|
{
|
||
|
sStatData* pStat = 0;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat)
|
||
|
{
|
||
|
switch (pStat->GetType())
|
||
|
{
|
||
|
case STAT_TYPE_TEXTLABEL: ++label_stats; break;
|
||
|
case STAT_TYPE_INT: pStat->IsObfuscated() ? ++obfint_stats : ++int_stats; break;
|
||
|
case STAT_TYPE_FLOAT: pStat->IsObfuscated() ? ++obffloat_stats : ++float_stats; break;
|
||
|
case STAT_TYPE_BOOLEAN: ++bool_stats; break;
|
||
|
case STAT_TYPE_UINT8: ++u8_stats; break;
|
||
|
case STAT_TYPE_UINT16: ++u16_stats; break;
|
||
|
case STAT_TYPE_UINT32: ++u32_stats; break;
|
||
|
case STAT_TYPE_UINT64: pStat->IsObfuscated() ? ++obfu64_stats : ++u64_stats; break;
|
||
|
case STAT_TYPE_DATE: ++date_stats; break;
|
||
|
case STAT_TYPE_POS: ++pos_stats; break;
|
||
|
case STAT_TYPE_STRING: ++string_stats; break;
|
||
|
case STAT_TYPE_PACKED: ++packed_stats; break;
|
||
|
case STAT_TYPE_USERID: ++userid_stats; break;
|
||
|
case STAT_TYPE_INT64: pStat->IsObfuscated() ? ++obfs64_stats : ++s64_stats; break;
|
||
|
case STAT_TYPE_PROFILE_SETTING: ++profilesetting_stats; break;
|
||
|
default: statAssertf(0, "Invalid stat tag type \"%d\"", pStat->GetType()); break;
|
||
|
}
|
||
|
|
||
|
if (pStat->GetDesc().GetIsProfileStat())
|
||
|
{
|
||
|
switch (pStat->GetType())
|
||
|
{
|
||
|
case STAT_TYPE_TEXTLABEL:
|
||
|
case STAT_TYPE_INT:
|
||
|
case STAT_TYPE_PROFILE_SETTING:
|
||
|
case STAT_TYPE_BOOLEAN:
|
||
|
case STAT_TYPE_UINT8:
|
||
|
case STAT_TYPE_UINT16:
|
||
|
++profileint_stats;
|
||
|
break;
|
||
|
case STAT_TYPE_FLOAT:
|
||
|
++profilefloat_stats;
|
||
|
break;
|
||
|
case STAT_TYPE_INT64:
|
||
|
case STAT_TYPE_UINT32:
|
||
|
case STAT_TYPE_UINT64:
|
||
|
case STAT_TYPE_DATE:
|
||
|
case STAT_TYPE_POS:
|
||
|
case STAT_TYPE_PACKED:
|
||
|
case STAT_TYPE_USERID:
|
||
|
++profilebigints_stats;
|
||
|
break;
|
||
|
|
||
|
case STAT_TYPE_STRING:
|
||
|
break;
|
||
|
|
||
|
default: statAssertf(0, "Invalid stat tag type \"%d\"", pStat->GetType()); break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
iter++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const unsigned profile_size = profileint_stats*4+profilefloat_stats*4+profilebigints_stats*12
|
||
|
+((profileint_stats+profilefloat_stats+profilebigints_stats)*4);
|
||
|
|
||
|
static unsigned total_size = int_stats*sizeof(sIntStatData)+label_stats*sizeof(sLabelStatData)+float_stats*sizeof(sFloatStatData)
|
||
|
+bool_stats*sizeof(sBoolStatData)+u8_stats*sizeof(sUns8StatData)+u16_stats*sizeof(sUns16StatData)
|
||
|
+u32_stats*sizeof(sUns32StatData)+u64_stats*sizeof(sUns64StatData)+date_stats*sizeof(sDateStatData)
|
||
|
+pos_stats*sizeof(sPosStatData)+string_stats*sizeof(sStringStatData)+packed_stats*sizeof(sPackedStatData)
|
||
|
+userid_stats*sizeof(sUserIdStatData)+profilesetting_stats*sizeof(sProfileSettingStatData)+s64_stats*sizeof(sInt64StatData)
|
||
|
+obfint_stats*sizeof(sObfIntStatData)+obfs64_stats*sizeof(sObfInt64StatData)+obfu64_stats*sizeof(sObfUns64StatData)
|
||
|
+obffloat_stats*sizeof(sObfFloatStatData);
|
||
|
|
||
|
|
||
|
statDebugf1("-----------------------------------------------------------------");
|
||
|
statDebugf1(" Number of stats = \"%d\"", m_aStatsData.GetCount());
|
||
|
statDebugf1(" sizeof Stats = \"%d\"", total_size);
|
||
|
statDebugf1(" sizeof Profile Stats = \"%d\" int=%d,float=%d,bigint=%d", profile_size, profileint_stats, profilefloat_stats, profilebigints_stats);
|
||
|
statDebugf1(" int = \"%d:%" SIZETFMT "d\"", int_stats, int_stats*sizeof(sIntStatData));
|
||
|
statDebugf1(" obfuscated int = \"%d:%" SIZETFMT "d\"", obfint_stats, obfint_stats*sizeof(sObfIntStatData));
|
||
|
statDebugf1(" labels = \"%d:%" SIZETFMT "d\"", label_stats, label_stats*sizeof(sLabelStatData));
|
||
|
statDebugf1(" float = \"%d:%" SIZETFMT "d\"", float_stats, float_stats*sizeof(sFloatStatData));
|
||
|
statDebugf1(" obfuscated float = \"%d:%" SIZETFMT "d\"", obffloat_stats,obffloat_stats*sizeof(sObfFloatStatData));
|
||
|
statDebugf1(" bool = \"%d:%" SIZETFMT "d\"", bool_stats, bool_stats*sizeof(sBoolStatData));
|
||
|
statDebugf1(" u8 = \"%d:%" SIZETFMT "d\"", u8_stats, u8_stats*sizeof(sUns8StatData));
|
||
|
statDebugf1(" u16 = \"%d:%" SIZETFMT "d\"", u16_stats, u16_stats*sizeof(sUns16StatData));
|
||
|
statDebugf1(" u32 = \"%d:%" SIZETFMT "d\"", u32_stats, u32_stats*sizeof(sUns32StatData));
|
||
|
statDebugf1(" u64 = \"%d:%" SIZETFMT "d\"", u64_stats, u64_stats*sizeof(sUns64StatData));
|
||
|
statDebugf1(" obfuscated u64 = \"%d:%" SIZETFMT "d\"", obfu64_stats, obfu64_stats*sizeof(sObfUns64StatData));
|
||
|
statDebugf1(" pos = \"%d:%" SIZETFMT "d\"", pos_stats, pos_stats*sizeof(sPosStatData));
|
||
|
statDebugf1(" date = \"%d:%" SIZETFMT "d\"", date_stats, date_stats*sizeof(sDateStatData));
|
||
|
statDebugf1(" string = \"%d:%" SIZETFMT "d\"", string_stats, string_stats*sizeof(sStringStatData));
|
||
|
statDebugf1(" packed = \"%d:%" SIZETFMT "d\"", packed_stats, packed_stats*sizeof(sPackedStatData));
|
||
|
statDebugf1(" profilesetting = \"%d:%" SIZETFMT "d\"", profilesetting_stats, profilesetting_stats*sizeof(sProfileSettingStatData));
|
||
|
statDebugf1(" s64 = \"%d:%" SIZETFMT "d\"", s64_stats, s64_stats*sizeof(sInt64StatData));
|
||
|
statDebugf1(" obfuscated s64 = \"%d:%" SIZETFMT "d\"", obfs64_stats, obfs64_stats*sizeof(sObfInt64StatData));
|
||
|
statDebugf1("-----------------------------------------------------------------");
|
||
|
|
||
|
//CompileTimeAssert(sizeof(sStatData) == 64); // 52 (inmap_node) + 12
|
||
|
//TestPosPack(1984.1984, -1900.1, -17.6789, "POSITION_TEST");
|
||
|
//TestDatePack(1984, 7, 23, 12, 00, 32, 927, "DATE_TEST");
|
||
|
|
||
|
#if __TEST_MASKED_STAT
|
||
|
TestMaskedStats();
|
||
|
#endif
|
||
|
|
||
|
if (returnProfileStats)
|
||
|
{
|
||
|
return profile_size;
|
||
|
}
|
||
|
|
||
|
return total_size;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_CheckProfileSyncConsistency()
|
||
|
{
|
||
|
if (!m_SyncConsistencyStatus.None())
|
||
|
{
|
||
|
profileStatDebugf("Consistency check is already in progress, skipping");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!CLiveManager::GetProfileStatsMgr().CanSynchronize())
|
||
|
{
|
||
|
profileStatDebugf("Consistency check can't run (sync is already in progress?)");
|
||
|
}
|
||
|
|
||
|
profileStatDebugf("Starting profile sync consistency check...");
|
||
|
profileStatDebugf("Client sync state:");
|
||
|
|
||
|
for (StatsMap::Iterator iter = m_aStatsData.Begin(); iter != m_aStatsData.End(); ++iter)
|
||
|
{
|
||
|
sStatData* pStat = NULL;
|
||
|
if (iter.GetKey().IsValid())
|
||
|
{
|
||
|
pStat = *iter;
|
||
|
}
|
||
|
|
||
|
if (pStat
|
||
|
&& pStat->GetDesc().GetIsProfileStat())
|
||
|
{
|
||
|
const char* synched = pStat->GetDesc().GetSynched() ? "true" : "false";
|
||
|
const char* dirty = pStat->GetDesc().GetDirty() ? "true" : "false";
|
||
|
|
||
|
profileStatDebugf("Stat (%s, Group %d) Synched: %s, Dirty: %s", iter.GetKeyName(), pStat->GetDesc().GetGroup(), synched, dirty);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Now kick off a full synchronize
|
||
|
m_SyncConsistencyStatus.Reset();
|
||
|
rlProfileStats::Synchronize(NetworkInterface::GetLocalGamerIndex(), &m_SyncConsistencyStatus);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CStatsDataMgr::Bank_Enable_Disable_Stats_Tracking()
|
||
|
{
|
||
|
CStatsUtils::EnableStatsTracking(!CStatsUtils::IsStatsTrackingEnabled());
|
||
|
}
|
||
|
|
||
|
#endif // __BANK
|
||
|
|
||
|
// EOF
|
||
|
|
||
|
|