// // 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=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(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(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(val->GetInt32()) == GetUInt8()); } else if (GetIsBaseType(STAT_TYPE_UINT16)) { result = (static_cast(val->GetInt32()) == GetUInt16()); } else if (GetIsBaseType(STAT_TYPE_UINT32)) { result = (static_cast(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(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(val->GetInt32())); } else if (GetIsBaseType(STAT_TYPE_UINT16)) { SetUInt16(static_cast(val->GetInt32())); } else if (GetIsBaseType(STAT_TYPE_UINT32)) { SetUInt32(static_cast(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(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(GetBoolean())); } else if (GetIsBaseType(STAT_TYPE_UINT8) || GetIsBaseType(STAT_TYPE_UINT16)) { val->SetInt32(GetIsBaseType(STAT_TYPE_UINT8) ? static_cast(GetUInt8()) : static_cast(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(GetUInt32()) : static_cast(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(GetBoolean())); } else if (GetIsBaseType(STAT_TYPE_UINT8) || GetIsBaseType(STAT_TYPE_UINT16)) { val->SetInt32(GetIsBaseType(STAT_TYPE_UINT8) ? static_cast(GetUInt8()) : static_cast(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(GetUInt32()) : static_cast(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(elapsedTime / 1000); data.m_LevelUpElapsedTime = (m_LevelUpElapsedTime > 0 && currentTime > m_LevelUpElapsedTime) ? static_cast((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; iGetDesc(); 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(newValue0) { 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(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(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; iGetHash(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((*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(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(pStatNextSlot); } } #endif // RLGAMERHANDLE_NP if (statparser.m_Node->GetElement().FindAttribute("Default")) { switch (statparser.m_Type) { case STAT_TYPE_STRING: { char* text = const_cast(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(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(PS_GRP_SP_1 + i)); } RegisterNewStat(statparser); } } else { u32 category = statparser.m_Desc.GetCategory(); for (int i=0; i 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(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; iGetChild()->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;iGetType()].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