first commit
This commit is contained in:
@@ -0,0 +1,717 @@
|
||||
#include "stdafx.h"
|
||||
#include "DurangoLeaderboardManager.h"
|
||||
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
||||
|
||||
namespace WFC = Windows::Foundation::Collections;
|
||||
namespace CC = concurrency;
|
||||
|
||||
LeaderboardManager *LeaderboardManager::m_instance = new DurangoLeaderboardManager(); //Singleton instance of the LeaderboardManager
|
||||
|
||||
DurangoLeaderboardManager::DurangoLeaderboardManager()
|
||||
{
|
||||
m_eStatsState = eStatsState_Idle;
|
||||
InitializeCriticalSection(&m_csStatsState);
|
||||
|
||||
m_openSessions = 0;
|
||||
m_xboxLiveContext = nullptr;
|
||||
m_scores = NULL;
|
||||
m_readCount = 0;
|
||||
m_maxRank = 0;
|
||||
m_waitingForProfiles = false;
|
||||
|
||||
m_difficulty = 0;
|
||||
m_type = eStatsType_UNDEFINED;
|
||||
m_statNames = ref new PC::Vector<P::String^>();
|
||||
m_xboxUserIds = ref new PC::Vector<P::String^>();
|
||||
m_leaderboardAsyncOp = nullptr;
|
||||
m_statsAsyncOp = nullptr;
|
||||
|
||||
for(unsigned int difficulty = 0; difficulty < 4; ++difficulty)
|
||||
{
|
||||
m_leaderboardNames[difficulty][eStatsType_Travelling] = L"LeaderboardTravelling" + _toString(difficulty);
|
||||
m_leaderboardNames[difficulty][eStatsType_Mining] = L"LeaderboardMining" + _toString(difficulty);
|
||||
m_leaderboardNames[difficulty][eStatsType_Farming] = L"LeaderboardFarming" + _toString(difficulty);
|
||||
m_leaderboardNames[difficulty][eStatsType_Kills] = L"LeaderboardKills" + _toString(difficulty);
|
||||
|
||||
m_socialLeaderboardNames[difficulty][eStatsType_Travelling] = L"Leaderboard.LeaderboardId.0.DifficultyLevelId." + _toString(difficulty);
|
||||
m_socialLeaderboardNames[difficulty][eStatsType_Mining] = L"Leaderboard.LeaderboardId.1.DifficultyLevelId." + _toString(difficulty);
|
||||
m_socialLeaderboardNames[difficulty][eStatsType_Farming] = L"Leaderboard.LeaderboardId.2.DifficultyLevelId." + _toString(difficulty);
|
||||
m_socialLeaderboardNames[difficulty][eStatsType_Kills] = L"Leaderboard.LeaderboardId.3.DifficultyLevelId." + _toString(difficulty);
|
||||
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.0"); // Walked
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.2"); // Fallen
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.4"); // Minecart
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.5"); // Boat
|
||||
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.3"); // Dirt
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.4"); // Cobblestone
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.12"); // Sand
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.1"); // Stone
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.13"); // Gravel
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.82"); // Clay
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.49"); // Obsidian
|
||||
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.1.ItemId.344"); // Eggs
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.59"); // Wheat
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.39"); // Mushroom
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.83"); // Sugarcane
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.2.ItemId.335"); // Milk
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.1.ItemId.86"); // Pumpkin
|
||||
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.54"); // Zombie
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.51"); // Skeleton
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.50"); // Creeper
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.52"); // Spider
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.49"); // Spider Jockey
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.57"); // Zombie Pigman
|
||||
m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.55"); // Slime
|
||||
}
|
||||
}
|
||||
|
||||
void DurangoLeaderboardManager::Tick()
|
||||
{
|
||||
ReadView view;
|
||||
|
||||
switch( getState() )
|
||||
{
|
||||
case eStatsState_GettingLeaderboardInfo:
|
||||
break;
|
||||
case eStatsState_ReceivedLeaderboardInfo:
|
||||
{
|
||||
setState(eStatsState_GettingStatsInfo);
|
||||
|
||||
// Get the actual display info for the stats
|
||||
m_statsAsyncOp = m_xboxLiveContext->UserStatisticsService->GetMultipleUserStatisticsAsync(
|
||||
m_xboxUserIds->GetView(), // the collection of Xbox user IDs whose stats we want to retrieve
|
||||
SERVICE_CONFIG_ID, // the service config that contains the stats we want
|
||||
m_statNames->GetView() // a list of stat names we want
|
||||
);
|
||||
|
||||
auto task = concurrency::create_task(m_statsAsyncOp).then( [this] (CC::task<WFC::IVectorView<MXS::UserStatistics::UserStatisticsResult^>^> resultListTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] Second continuation\n");
|
||||
m_statsAsyncOp = nullptr;
|
||||
|
||||
WFC::IVectorView<MXS::UserStatistics::UserStatisticsResult^>^ resultList = resultListTask.get();
|
||||
|
||||
if (m_xboxLiveContext == nullptr) throw(ref new P::Exception(-1));
|
||||
|
||||
int userIndex = 0;
|
||||
for( MXS::UserStatistics::UserStatisticsResult^ result : resultList )
|
||||
{
|
||||
app.DebugPrintf("XboxUserId: %ls\n", result->XboxUserId->Data());
|
||||
|
||||
for( UINT index = 0; index<result->ServiceConfigurationStatistics->Size; index++ )
|
||||
{
|
||||
MXS::UserStatistics::ServiceConfigurationStatistic^ configStat = result->ServiceConfigurationStatistics->GetAt(index);
|
||||
//app.DebugPrintf("ServiceConfigurationId: %ls\n", configStat->ServiceConfigurationId->Data());
|
||||
|
||||
updateStatsInfo(userIndex, m_difficulty, m_type, configStat->Statistics);
|
||||
}
|
||||
++userIndex;
|
||||
}
|
||||
|
||||
app.DebugPrintf("[LeaderboardManager] Setting to ready\n");
|
||||
setState(eStatsState_Ready);
|
||||
|
||||
}
|
||||
catch (Platform::Exception^ ex)
|
||||
{
|
||||
m_leaderboardAsyncOp = nullptr;
|
||||
setState(eStatsState_Failed);
|
||||
|
||||
if (ex->HResult == HTTP_E_STATUS_NOT_FOUND) app.DebugPrintf("[LeaderboardManager] ERROR calling GetLeaderboardAsync: 404 Not Found - 0x%0.8x\n", ex->HResult);
|
||||
else app.DebugPrintf("[LeaderboardManager] ERROR calling GetLeaderboardAsync: 0x%0.8x\n", ex->HResult);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] SecondContinuation: Unknown exception.\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case eStatsState_GettingStatsInfo:
|
||||
break;
|
||||
case eStatsState_Ready:
|
||||
{
|
||||
// If we're waiting on profiles, don't return scores just yet
|
||||
if (m_waitingForProfiles)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_displayNames.size() == m_readCount)
|
||||
{
|
||||
// Add display names to scores
|
||||
for (int i = 0; i < m_displayNames.size(); i++)
|
||||
{
|
||||
m_scores[i].m_name = m_displayNames[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This seem to happen if something went wrong with Xbox Live
|
||||
app.DebugPrintf("DurangoLeaderboardManager::Tick: Display names count (%i) didn't match read count (%i)", m_displayNames.size(), m_readCount);
|
||||
}
|
||||
|
||||
m_displayNames.clear();
|
||||
}
|
||||
|
||||
//assert(m_scores != NULL || m_readCount == 0);
|
||||
|
||||
view.m_numQueries = m_readCount;
|
||||
view.m_queries = m_scores;
|
||||
|
||||
// 4J-JEV: Debugging.
|
||||
//LeaderboardManager::printStats(view);
|
||||
|
||||
eStatsReturn ret = view.m_numQueries > 0 ? eStatsReturn_Success : eStatsReturn_NoResults;
|
||||
|
||||
if (m_readListener != NULL)
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] OnStatsReadComplete(%i, %i, _)\n", ret, m_readCount);
|
||||
m_readListener->OnStatsReadComplete(ret, m_maxRank, view);
|
||||
}
|
||||
|
||||
app.DebugPrintf("[LeaderboardManager] Deleting scores\n");
|
||||
delete [] m_scores;
|
||||
m_scores = NULL;
|
||||
|
||||
setState(eStatsState_Idle);
|
||||
}
|
||||
break;
|
||||
|
||||
case eStatsState_Failed:
|
||||
view.m_numQueries = 0;
|
||||
view.m_queries = NULL;
|
||||
|
||||
if ( m_readListener != NULL )
|
||||
{
|
||||
m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, 0, view);
|
||||
}
|
||||
|
||||
setState(eStatsState_Idle);
|
||||
|
||||
break;
|
||||
|
||||
case eStatsState_Canceled:
|
||||
app.DebugPrintf("[LeaderboardManager] Setting canceled\n");
|
||||
setState(eStatsState_Idle);
|
||||
break;
|
||||
|
||||
default: // Getting or Idle.
|
||||
if (m_openSessions == 0 && m_xboxLiveContext != nullptr)
|
||||
{
|
||||
m_xboxLiveContext = nullptr;
|
||||
|
||||
app.DebugPrintf("[LeaderboardManager] Tick(): Nulled XboxLiveContext\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Open a session
|
||||
bool DurangoLeaderboardManager::OpenSession()
|
||||
{
|
||||
if (m_openSessions == 0)
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] OpenSession()\n");
|
||||
|
||||
try
|
||||
{
|
||||
WXS::User^ user = ProfileManager.GetUser(ProfileManager.GetPrimaryPad());
|
||||
if(user != nullptr && user->IsSignedIn && !user->IsGuest)
|
||||
{
|
||||
m_xboxLiveContext = ref new MXS::XboxLiveContext(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] OpenSession(): Failed to created new XboxLiveContext, No valid user\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Platform::Exception^ ex)
|
||||
{
|
||||
m_xboxLiveContext = nullptr;
|
||||
app.DebugPrintf("[LeaderboardManager] OpenSession(): Failed to created new XboxLiveContext, ret == 0x%08X.\n", ex->HResult);
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] OpenSession(): Unknown exception.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else app.DebugPrintf("[LeaderboardManager] OpenSession(): Another session opened, total=%i\n", m_openSessions+1);
|
||||
|
||||
m_openSessions++;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Close a session
|
||||
void DurangoLeaderboardManager::CloseSession()
|
||||
{
|
||||
m_openSessions--;
|
||||
|
||||
if (m_openSessions == 0)
|
||||
{
|
||||
if(isIdle())
|
||||
{
|
||||
m_xboxLiveContext = nullptr;
|
||||
|
||||
app.DebugPrintf("[LeaderboardManager] CloseSession(): Nulled XboxLiveContext\n");
|
||||
}
|
||||
}
|
||||
else app.DebugPrintf("[LeaderboardManager] CloseSession(): %i sessions still open.\n", m_openSessions);
|
||||
}
|
||||
|
||||
//Delete a session
|
||||
void DurangoLeaderboardManager::DeleteSession()
|
||||
{
|
||||
}
|
||||
|
||||
//Write the given stats
|
||||
//This is called synchronously and will not free any memory allocated for views when it is done
|
||||
|
||||
bool DurangoLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DurangoLeaderboardManager::ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount)
|
||||
{
|
||||
// Need to cancel read/write operation first.
|
||||
if (!isIdle()) return false;
|
||||
if (!LeaderboardManager::ReadStats_Friends(listener, difficulty, type, myUID, startIndex, readCount)) return false;
|
||||
setState(eStatsState_GettingLeaderboardInfo);
|
||||
|
||||
if( m_xboxLiveContext == nullptr )
|
||||
{
|
||||
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
|
||||
}
|
||||
|
||||
// Request the leaderboard to get ranking information
|
||||
auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardForSocialGroupWithSkipToRankAsync(
|
||||
ref new P::String(myUID.toString().c_str()),
|
||||
SERVICE_CONFIG_ID,
|
||||
ref new P::String(m_socialLeaderboardNames[difficulty][type].c_str()),
|
||||
MXS::Social::SocialGroupConstants::People,
|
||||
startIndex,
|
||||
ref new P::String(L"descending"),
|
||||
readCount
|
||||
);
|
||||
|
||||
runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_Friends);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DurangoLeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount)
|
||||
{
|
||||
// Need to cancel read/write operation first.
|
||||
if (!isIdle()) return false;
|
||||
if (!LeaderboardManager::ReadStats_MyScore(listener, difficulty, type, myUID, readCount)) return false;
|
||||
setState(eStatsState_GettingLeaderboardInfo);
|
||||
|
||||
if( m_xboxLiveContext == nullptr )
|
||||
{
|
||||
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
|
||||
}
|
||||
|
||||
P::String^ leaderboardName = ref new P::String(m_leaderboardNames[difficulty][type].c_str());
|
||||
|
||||
// Request the leaderboard to get ranking information
|
||||
auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardWithSkipToUserAsync(
|
||||
SERVICE_CONFIG_ID,
|
||||
leaderboardName,
|
||||
ref new P::String(myUID.toString().c_str()), // skip to this user
|
||||
readCount
|
||||
);
|
||||
|
||||
runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_MyScore);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DurangoLeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount)
|
||||
{
|
||||
// Need to cancel read/write operation first.
|
||||
if (!isIdle()) return false;
|
||||
if (!LeaderboardManager::ReadStats_TopRank(listener, difficulty, type, startIndex, readCount)) return false;
|
||||
setState(eStatsState_GettingLeaderboardInfo);
|
||||
|
||||
if( m_xboxLiveContext == nullptr )
|
||||
{
|
||||
throw ref new Platform::InvalidArgumentException(L"A valid User is required");
|
||||
}
|
||||
|
||||
P::String^ leaderboardName = ref new P::String(m_leaderboardNames[difficulty][type].c_str());
|
||||
|
||||
// Request the leaderboard to get ranking information
|
||||
auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardAsync(
|
||||
SERVICE_CONFIG_ID,
|
||||
leaderboardName,
|
||||
startIndex, // skip this many ranks
|
||||
readCount
|
||||
);
|
||||
|
||||
runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_TopRank);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Perform a flush of the stats
|
||||
void DurangoLeaderboardManager::FlushStats()
|
||||
{
|
||||
}
|
||||
|
||||
//Cancel the current operation
|
||||
void DurangoLeaderboardManager::CancelOperation()
|
||||
{
|
||||
m_readListener = NULL;
|
||||
setState(eStatsState_Canceled);
|
||||
|
||||
if(m_leaderboardAsyncOp) m_leaderboardAsyncOp->Cancel();
|
||||
if(m_statsAsyncOp) m_statsAsyncOp->Cancel();
|
||||
|
||||
//if (m_transactionCtx != 0)
|
||||
//{
|
||||
// int ret = sceNpScoreAbortTransaction(m_transactionCtx);
|
||||
//
|
||||
// if (ret < 0)
|
||||
// {
|
||||
// app.DebugPrintf("[LeaderboardManager] CancelOperation(): Problem encountered aborting current operation, 0x%X.\n", ret);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// app.DebugPrintf("[LeaderboardManager] CancelOperation(): Operation aborted successfully.\n");
|
||||
// }
|
||||
//}
|
||||
//else app.DebugPrintf("[LeaderboardManager] CancelOperation(): No current operation.\n");
|
||||
}
|
||||
|
||||
//Is the leaderboard manager idle.
|
||||
bool DurangoLeaderboardManager::isIdle()
|
||||
{
|
||||
return getState() == eStatsState_Idle;
|
||||
}
|
||||
|
||||
void DurangoLeaderboardManager::runLeaderboardRequest(WF::IAsyncOperation<MXSL::LeaderboardResult^>^ asyncOp, int difficulty, EStatsType type, unsigned int readCount, EFilterMode filter)
|
||||
{
|
||||
m_leaderboardAsyncOp = asyncOp;
|
||||
m_difficulty = difficulty;
|
||||
m_type = type;
|
||||
|
||||
// Build stat names
|
||||
m_statNames = ref new PC::Vector<P::String^>();
|
||||
m_statNames->Clear();
|
||||
for(wstring name : m_leaderboardStatNames[difficulty][type])
|
||||
{
|
||||
m_statNames->Append(ref new P::String(name.c_str()));
|
||||
}
|
||||
|
||||
app.DebugPrintf("[LeaderboardManager] Running request\n");
|
||||
CC::create_task(asyncOp)
|
||||
.then( [this, readCount, difficulty, type, filter] (CC::task<MXSL::LeaderboardResult^> resultTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] First continuation.\n");
|
||||
|
||||
m_leaderboardAsyncOp = nullptr;
|
||||
|
||||
MXSL::LeaderboardResult^ lastResult = resultTask.get();
|
||||
|
||||
app.DebugPrintf(
|
||||
"Name: %ls - Filter: %ls - Rows: Retrieved=%d Total=%d Requested=%d.\n",
|
||||
lastResult->DisplayName->Data(),
|
||||
LeaderboardManager::filterNames[ (int) filter ].c_str(),
|
||||
lastResult->Rows->Size, lastResult->TotalRowCount, readCount
|
||||
);
|
||||
|
||||
//app.DebugPrintf("Column count: %d, Column: %ls, %ls, %d\n", lastResult->Columns->Size, lastResult->Columns->GetAt(0)->DisplayName->Data(), lastResult->Columns->GetAt(0)->StatisticName->Data(), lastResult->Columns->GetAt(0)->StatisticType);
|
||||
|
||||
// 4J-JEV: Fix for Xbox One #162541 - [CRASH] Code: Leaderboards: Title crashes after pressing [B] Back button while changing Leaderboards' filter to 'My Score'
|
||||
// 4J-JEV: Fix for X1: #165487 - [CRASH] XR-074: Compliance: The title does not properly handle switching to offline session while browsing the Leaderboards.
|
||||
if (m_xboxLiveContext == nullptr) throw(ref new P::Exception(-1));
|
||||
|
||||
// If this is My_Score, check that the user appears
|
||||
if (filter == eFM_MyScore)
|
||||
{
|
||||
bool userIncluded = false;
|
||||
for(int i = 0; i < lastResult->Rows->Size; i++)
|
||||
{
|
||||
if (lastResult->Rows->GetAt(i)->XboxUserId == m_xboxLiveContext->User->XboxUserId) userIncluded = true;
|
||||
}
|
||||
|
||||
// If the user isn't included, don't show the results
|
||||
if (!userIncluded)
|
||||
{
|
||||
m_readCount = 0;
|
||||
throw(ref new P::Exception(INET_E_DATA_NOT_AVAILABLE));
|
||||
}
|
||||
}
|
||||
|
||||
m_maxRank = lastResult->TotalRowCount;
|
||||
m_readCount = lastResult->Rows->Size;
|
||||
|
||||
if (m_scores != NULL) delete [] m_scores;
|
||||
m_scores = new ReadScore[m_readCount];
|
||||
ZeroMemory(m_scores, sizeof(ReadScore) * m_readCount);
|
||||
|
||||
m_xboxUserIds->Clear();
|
||||
|
||||
app.DebugPrintf("[LeaderboardManager] Retrieved Scores:\n");
|
||||
for( UINT index = 0; index < lastResult->Rows->Size; index++ )
|
||||
{
|
||||
MXSL::LeaderboardRow^ row = lastResult->Rows->GetAt(index);
|
||||
|
||||
app.DebugPrintf(
|
||||
"\tIndex: %d\tRank: %d\tPercentile: %.1f%%\tXboxUserId: %ls\tValue: %ls.\n",
|
||||
index,
|
||||
row->Rank,
|
||||
row->Percentile * 100,
|
||||
row->XboxUserId->Data(),
|
||||
row->Values->GetAt(0)->Data()
|
||||
);
|
||||
|
||||
m_scores[index].m_name = row->Gamertag->Data();
|
||||
m_scores[index].m_rank = row->Rank;
|
||||
m_scores[index].m_uid = PlayerUID(row->XboxUserId->Data());
|
||||
|
||||
// 4J-JEV: Added to help determine if this player's score is hidden due to their privacy settings.
|
||||
m_scores[index].m_totalScore = (unsigned long) _fromString<long long>(row->Values->GetAt(0)->Data());
|
||||
|
||||
m_xboxUserIds->Append(row->XboxUserId);
|
||||
}
|
||||
|
||||
if(m_readCount > 0)
|
||||
{
|
||||
vector<PlayerUID> xuids = vector<PlayerUID>();
|
||||
for(int i = 0; i < lastResult->Rows->Size; i++)
|
||||
{
|
||||
xuids.push_back(PlayerUID(lastResult->Rows->GetAt(i)->XboxUserId->Data()));
|
||||
}
|
||||
m_waitingForProfiles = true;
|
||||
ProfileManager.GetProfiles(xuids, &GetProfilesCallback, this);
|
||||
setState(eStatsState_ReceivedLeaderboardInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we hit the end of the list
|
||||
app.DebugPrintf("Reach the end\n");
|
||||
setState(eStatsState_Ready);
|
||||
}
|
||||
}
|
||||
catch (Platform::Exception^ ex)
|
||||
{
|
||||
m_leaderboardAsyncOp = nullptr;
|
||||
if (ex->HResult == INET_E_DATA_NOT_AVAILABLE)
|
||||
{
|
||||
// we hit the end of the list
|
||||
app.DebugPrintf("ERROR: Reach the end\n");
|
||||
setState(eStatsState_Ready);
|
||||
}
|
||||
else if(ex->HResult == HTTP_E_STATUS_NOT_FOUND)
|
||||
{
|
||||
app.DebugPrintf("Error calling GetLeaderboardAsync function: 404 Not Found - 0x%0.8x\n", ex->HResult);
|
||||
setState(eStatsState_Failed);
|
||||
}
|
||||
else
|
||||
{
|
||||
app.DebugPrintf("Error calling GetLeaderboardAsync function: 0x%0.8x\n", ex->HResult);
|
||||
setState(eStatsState_Failed);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] Unknown exception.\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DurangoLeaderboardManager::updateStatsInfo(int userIndex, int difficulty, EStatsType type, WFC::IVectorView<MXS::UserStatistics::Statistic^>^ statsResult)
|
||||
{
|
||||
if (m_scores)
|
||||
{
|
||||
LeaderboardManager::ReadScore *userScore = &m_scores[userIndex];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case eStatsType_Farming: userScore->m_statsSize = 6; break;
|
||||
case eStatsType_Mining: userScore->m_statsSize = 7; break;
|
||||
case eStatsType_Kills: userScore->m_statsSize = 7; break;
|
||||
case eStatsType_Travelling: userScore->m_statsSize = 4; break;
|
||||
}
|
||||
|
||||
int statIndex = 0, sumScores = 0;
|
||||
for(wstring statName : m_leaderboardStatNames[difficulty][type])
|
||||
{
|
||||
bool found = false;
|
||||
for(auto result : statsResult)
|
||||
{
|
||||
if(statName.compare(result->StatisticName->Data()) == 0)
|
||||
{
|
||||
userScore->m_statsData[statIndex] = _fromString<unsigned long>(result->Value->Data());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
{
|
||||
userScore->m_statsData[statIndex] = 0;
|
||||
}
|
||||
|
||||
sumScores += userScore->m_statsData[statIndex];
|
||||
++statIndex;
|
||||
}
|
||||
|
||||
if ( (sumScores == 0) && (userScore->m_totalScore > 0) )
|
||||
{
|
||||
app.DebugPrintf("[LeaderboardManager] Player '%s' (rank %d) likely has privacy settings enabled.\n", userScore->m_name.c_str(), userScore->m_rank);
|
||||
userScore->m_idsErrorMessage = IDS_LEADERBOARD_SCORE_HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DurangoLeaderboardManager::GetProfilesCallback(LPVOID param, std::vector<Microsoft::Xbox::Services::Social::XboxUserProfile^> profiles)
|
||||
{
|
||||
DurangoLeaderboardManager *dlm = (DurangoLeaderboardManager *)param;
|
||||
|
||||
app.DebugPrintf("[LeaderboardManager] GetProfilesCallback, profiles.size() == %d.\n", profiles.size());
|
||||
|
||||
if (profiles.size() > 0)
|
||||
{
|
||||
dlm->m_displayNames = vector<wstring>();
|
||||
for (int i = 0; i < profiles.size(); i++)
|
||||
{
|
||||
dlm->m_displayNames.push_back(profiles[i]->GameDisplayName->Data());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dlm->setState(eStatsState_Failed);
|
||||
}
|
||||
|
||||
dlm->m_waitingForProfiles = false;
|
||||
}
|
||||
|
||||
DurangoLeaderboardManager::EStatsState DurangoLeaderboardManager::getState()
|
||||
{
|
||||
return m_eStatsState;
|
||||
}
|
||||
|
||||
void DurangoLeaderboardManager::setState(EStatsState newState)
|
||||
{
|
||||
EnterCriticalSection(&m_csStatsState);
|
||||
|
||||
bool validTransition = false;
|
||||
|
||||
switch(m_eStatsState)
|
||||
{
|
||||
case eStatsState_Idle:
|
||||
switch(newState)
|
||||
{
|
||||
case eStatsState_GettingLeaderboardInfo:
|
||||
validTransition = true;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case eStatsState_GettingLeaderboardInfo:
|
||||
switch(newState)
|
||||
{
|
||||
case eStatsState_Ready:
|
||||
case eStatsState_ReceivedLeaderboardInfo:
|
||||
case eStatsState_Canceled:
|
||||
case eStatsState_Failed:
|
||||
validTransition = true;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
break;
|
||||
case eStatsState_ReceivedLeaderboardInfo:
|
||||
switch(newState)
|
||||
{
|
||||
case eStatsState_GettingStatsInfo:
|
||||
case eStatsState_Canceled:
|
||||
case eStatsState_Failed:
|
||||
validTransition = true;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case eStatsState_GettingStatsInfo:
|
||||
switch(newState)
|
||||
{
|
||||
case eStatsState_Ready:
|
||||
case eStatsState_Canceled:
|
||||
case eStatsState_Failed:
|
||||
validTransition = true;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case eStatsState_Failed:
|
||||
switch(newState)
|
||||
{
|
||||
case eStatsState_Idle:
|
||||
validTransition = true;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case eStatsState_Ready:
|
||||
switch(newState)
|
||||
{
|
||||
case eStatsState_Canceled:
|
||||
case eStatsState_Idle:
|
||||
case eStatsState_Failed:
|
||||
validTransition = true;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case eStatsState_Canceled:
|
||||
switch(newState)
|
||||
{
|
||||
case eStatsState_Ready:
|
||||
newState = eStatsState_Idle;
|
||||
case eStatsState_Idle:
|
||||
validTransition = true;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
};
|
||||
|
||||
#ifndef _CONTENT_PACKAGE
|
||||
app.DebugPrintf(
|
||||
"[LeaderboardManager] %s state transition:\t%ls(%d) -> %ls(%d).\n",
|
||||
(validTransition ? "Valid" : "INVALID"),
|
||||
stateToString(m_eStatsState).c_str(), m_eStatsState,
|
||||
stateToString(newState).c_str(), newState
|
||||
);
|
||||
#endif
|
||||
|
||||
if (validTransition)
|
||||
{
|
||||
m_eStatsState = newState;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&m_csStatsState);
|
||||
}
|
||||
|
||||
wstring DurangoLeaderboardManager::stateToString(EStatsState eState)
|
||||
{
|
||||
switch (eState)
|
||||
{
|
||||
case eStatsState_Idle: return L"eStatsState_Idle";
|
||||
case eStatsState_GettingLeaderboardInfo: return L"eStatsState_GettingLeaderboardInfo";
|
||||
case eStatsState_ReceivedLeaderboardInfo: return L"eStatsState_ReceivedLeaderboardInfo";
|
||||
case eStatsState_GettingStatsInfo: return L"eStatsState_GettingStatsInfo";
|
||||
case eStatsState_ReceivedStatsInfo: return L"eStatsState_ReceivedStatsInfo";
|
||||
case eStatsState_Failed: return L"eStatsState_Failed";
|
||||
case eStatsState_Ready: return L"eStatsState_Ready";
|
||||
case eStatsState_Canceled: return L"eStatsState_Canceled";
|
||||
case eStatsState_Max: return L"eStatsState_MAX";
|
||||
default: return L"UNKNOWN";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common\Leaderboards\LeaderboardManager.h"
|
||||
|
||||
namespace P = Platform;
|
||||
namespace PC = Platform::Collections;
|
||||
namespace WF = Windows::Foundation;
|
||||
namespace WFC = Windows::Foundation::Collections;
|
||||
namespace MXSL = Microsoft::Xbox::Services::Leaderboard;
|
||||
|
||||
class DurangoLeaderboardManager : public LeaderboardManager
|
||||
{
|
||||
protected:
|
||||
enum EStatsState
|
||||
{
|
||||
eStatsState_Idle,
|
||||
eStatsState_GettingLeaderboardInfo,
|
||||
eStatsState_ReceivedLeaderboardInfo,
|
||||
eStatsState_GettingStatsInfo,
|
||||
eStatsState_ReceivedStatsInfo,
|
||||
eStatsState_Failed,
|
||||
eStatsState_Ready,
|
||||
eStatsState_Canceled,
|
||||
//eStatsState_Writing,
|
||||
eStatsState_Max
|
||||
};
|
||||
|
||||
private:
|
||||
unsigned short m_openSessions;
|
||||
|
||||
CRITICAL_SECTION m_csStatsState;
|
||||
EStatsState m_eStatsState; //State of the stats read
|
||||
|
||||
ReadScore *m_scores;
|
||||
unsigned int m_maxRank;
|
||||
|
||||
MXS::XboxLiveContext^ m_xboxLiveContext;
|
||||
wstring m_leaderboardNames[4][eStatsType_MAX];
|
||||
wstring m_socialLeaderboardNames[4][eStatsType_MAX];
|
||||
std::vector<wstring> m_leaderboardStatNames[4][eStatsType_MAX];
|
||||
|
||||
// Display names for the current scores
|
||||
std::vector<wstring> m_displayNames;
|
||||
bool m_waitingForProfiles;
|
||||
|
||||
int m_difficulty;
|
||||
EStatsType m_type;
|
||||
PC::Vector<P::String^>^ m_statNames;
|
||||
PC::Vector<P::String^>^ m_xboxUserIds;
|
||||
WF::IAsyncOperation<MXSL::LeaderboardResult^>^ m_leaderboardAsyncOp;
|
||||
WF::IAsyncOperation<WFC::IVectorView<Microsoft::Xbox::Services::UserStatistics::UserStatisticsResult^ >^ >^ m_statsAsyncOp;
|
||||
|
||||
public:
|
||||
DurangoLeaderboardManager();
|
||||
|
||||
virtual void Tick();
|
||||
|
||||
//Open a session
|
||||
virtual bool OpenSession();
|
||||
|
||||
//Close a session
|
||||
virtual void CloseSession();
|
||||
|
||||
//Delete a session
|
||||
virtual void DeleteSession();
|
||||
|
||||
//Write the given stats
|
||||
//This is called synchronously and will not free any memory allocated for views when it is done
|
||||
|
||||
virtual bool WriteStats(unsigned int viewCount, ViewIn views);
|
||||
|
||||
virtual bool ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount);
|
||||
virtual bool ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount);
|
||||
virtual bool ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount);
|
||||
|
||||
//Perform a flush of the stats
|
||||
virtual void FlushStats();
|
||||
|
||||
//Cancel the current operation
|
||||
virtual void CancelOperation();
|
||||
|
||||
//Is the leaderboard manager idle.
|
||||
virtual bool isIdle();
|
||||
|
||||
static void GetProfilesCallback(LPVOID param, std::vector<Microsoft::Xbox::Services::Social::XboxUserProfile^> profiles);
|
||||
|
||||
private:
|
||||
void runLeaderboardRequest(WF::IAsyncOperation<MXSL::LeaderboardResult^>^ asyncOp, int difficulty, EStatsType type, unsigned int readCount, EFilterMode filter);
|
||||
void updateStatsInfo(int userIndex, int difficulty, EStatsType type, WFC::IVectorView<MXS::UserStatistics::Statistic^>^ statsResult);
|
||||
|
||||
EStatsState getState();
|
||||
void setState(EStatsState newState);
|
||||
wstring stateToString(EStatsState eState);
|
||||
};
|
||||
487
Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.cpp
Normal file
487
Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.cpp
Normal file
@@ -0,0 +1,487 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "..\Minecraft.World\Tile.h"
|
||||
#include "..\Minecraft.World\Item.h"
|
||||
|
||||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
|
||||
#include "..\Minecraft.World\EntityIO.h"
|
||||
|
||||
#include "..\Minecraft.World\StringHelpers.h"
|
||||
|
||||
#include "Common\Console_Awards_enum.h"
|
||||
|
||||
#include "DurangoStatsDebugger.h"
|
||||
|
||||
namespace WFC = Windows::Foundation::Collections;
|
||||
namespace CC = concurrency;
|
||||
|
||||
StatParam::StatParam(const wstring &base)
|
||||
{
|
||||
m_base = base;
|
||||
//m_numArgs = numArgs;
|
||||
m_args = vector<int>();
|
||||
|
||||
unsigned int count=0;
|
||||
wstring::size_type pos =base.find(L"*");
|
||||
while(pos!=string::npos)
|
||||
{
|
||||
count++;
|
||||
pos=base.find(L"*",pos+1);
|
||||
}
|
||||
|
||||
m_numArgs = count;
|
||||
}
|
||||
|
||||
void StatParam::addArgs(int v1, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
va_start(argptr, v1);
|
||||
m_args.push_back(v1);
|
||||
for (int i=0; i<(m_numArgs-1); i++)
|
||||
{
|
||||
int vi = va_arg(argptr, int);
|
||||
m_args.push_back(vi);
|
||||
}
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
vector<wstring> *StatParam::getStats()
|
||||
{
|
||||
vector<wstring> *out = new vector<wstring>();
|
||||
|
||||
static const int MAXSIZE = 256;
|
||||
static const wstring SUBSTR = L"*";
|
||||
|
||||
wstring wstr_itr, wstr_num;
|
||||
|
||||
if (m_args.size() <= 0 || m_numArgs <= 0)
|
||||
{
|
||||
out->push_back(m_base);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < m_args.size(); i += m_numArgs)
|
||||
{
|
||||
wstr_itr = m_base;
|
||||
|
||||
if (m_numArgs > 0)
|
||||
{
|
||||
for (int j=0; j<m_numArgs; j++)
|
||||
{
|
||||
wstr_num = to_wstring(m_args.at(i+j));
|
||||
size_t sz = wstr_itr.find(SUBSTR);
|
||||
|
||||
if (sz != wstring::npos)
|
||||
wstr_itr.replace(sz, SUBSTR.length(), wstr_num);
|
||||
}
|
||||
}
|
||||
|
||||
out->push_back( wstr_itr );
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
DurangoStatsDebugger *DurangoStatsDebugger::instance = NULL;
|
||||
|
||||
DurangoStatsDebugger::DurangoStatsDebugger()
|
||||
{
|
||||
InitializeCriticalSection(&m_retrievedStatsLock);
|
||||
}
|
||||
|
||||
vector<wstring> *DurangoStatsDebugger::getStats()
|
||||
{
|
||||
vector<wstring> *out = new vector<wstring>();
|
||||
|
||||
for (auto it = m_stats.begin(); it!=m_stats.end(); it++)
|
||||
{
|
||||
vector<wstring> *sublist = (*it)->getStats();
|
||||
out->insert(out->end(), sublist->begin(), sublist->end());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
DurangoStatsDebugger *DurangoStatsDebugger::Initialize()
|
||||
{
|
||||
DurangoStatsDebugger *out = new DurangoStatsDebugger();
|
||||
|
||||
StatParam *sp = new StatParam(L"McItemAcquired.AcquisitionMethodId.*.ItemId.*.ItemAux.*");
|
||||
sp->addArgs(1, Tile::dirt_Id, 0); // works
|
||||
sp->addArgs(2, Tile::dirt_Id, 0); // works
|
||||
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 0); // fixed (+ach 'Rainbow Collection')
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 1);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 2);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 3);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 4);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 5);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 6);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 7);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 8);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 9);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 10);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 11);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 12);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 13);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 14);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::cloth_Id, 15);
|
||||
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"McItemAcquired.AcquisitionMethodId.*.ItemId.*");
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Tile::dirt_Id); // works
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::milk_Id); // works.
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Tile::dirt_Id); // works.
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::porkChop_cooked_Id); // BROKEN! (ach 'Pork Chop' configured incorrectly)
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::cake_Id); // works. (ach 'The Lie' configured incorrectly)
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Bought, Item::emerald_Id); // fixed (+ach)
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::ironIngot_Id); // works. (+ach 'Acquired Hardware')
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Pickedup, Item::fish_raw_Id); // works. (+ach 'Delicious Fish')
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::fish_cooked_Id); // works. (+ach 'Delicious Fish')
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::sign_Id);
|
||||
sp->addArgs(DsItemEvent::eAcquisitionMethod_Crafted, Item::flowerPot_Id); // FIXING!
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"McItemAcquired.DifficultyLevelId.*.AcquisitionMethodId.*.ItemId.*");
|
||||
sp->addArgs(1, 1, Tile::dirt_Id); // works
|
||||
sp->addArgs(2, 2, Tile::dirt_Id); // works
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"McItemUsed.ItemId.*.ItemAux.*");
|
||||
//sp->addArgs(Item::apple_Id, 0);
|
||||
//sp->addArgs(Item::cake_Id, 0);
|
||||
sp->addArgs(Item::beef_raw_Id, 0); // works
|
||||
sp->addArgs(Item::porkChop_cooked_Id, 0); // works
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MinHungerWhenEaten.ItemId.*");
|
||||
//sp->addArgs(Item::apple_Id);
|
||||
//sp->addArgs(Item::cake_Id);
|
||||
sp->addArgs(Item::beef_raw_Id); // works
|
||||
sp->addArgs(Item::rotten_flesh_Id); // works (+ach IronBelly)
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"BlockBroken.BlockId.*");
|
||||
sp->addArgs( Tile::dirt_Id );
|
||||
sp->addArgs( Tile::rock_Id );
|
||||
sp->addArgs( Tile::emeraldOre_Id );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"BlockBroken.BlockId.*.BlockAux.*");
|
||||
sp->addArgs( Tile::dirt_Id, 0 );
|
||||
sp->addArgs( Tile::rock_Id, 0 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"BlockBroken.DifficultyLevelId.*.BlockId.*");
|
||||
sp->addArgs( 1, Tile::dirt_Id );
|
||||
sp->addArgs( 2, Tile::dirt_Id );
|
||||
sp->addArgs( 1, Tile::rock_Id );
|
||||
sp->addArgs( 2, Tile::rock_Id );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"BlockBroken");
|
||||
sp->addArgs( -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"BlockPlaced.BlockId.*");
|
||||
sp->addArgs( Tile::dirt_Id );
|
||||
sp->addArgs( Tile::stoneBrick_Id );
|
||||
sp->addArgs( Tile::sand_Id ); // works
|
||||
sp->addArgs( Tile::sign_Id ); // fixed
|
||||
sp->addArgs( Tile::wallSign_Id ); // fixed
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MobKilled.KillTypeId.*.EnemyRoleId.*.PlayerWeaponId.*"); // BROKEN!
|
||||
sp->addArgs( /*MELEE*/ 0, ioid_Cow, 0 );
|
||||
sp->addArgs( /*MELEE*/ 0, ioid_Cow, Item::sword_stone_Id );
|
||||
sp->addArgs( /*MELEE*/ 0, ioid_Pig, Item::sword_stone_Id );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MaxKillDistance.KillTypeId.*.EnemyRoleId.*.PlayerWeaponId.*"); // BROKEN!
|
||||
sp->addArgs( /*MELEE*/ 0, ioid_Cow, Item::sword_stone_Id );
|
||||
sp->addArgs( /*MELEE*/ 0, ioid_Pig, Item::sword_stone_Id );
|
||||
sp->addArgs( /*RANGE*/ 1, ioid_Creeper, ioid_Arrow ); // FIXING!
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MobKilled.KillTypeId.*.EnemyRoleId.*"); // BROKEN!
|
||||
sp->addArgs( /*MELEE*/ 0, ioid_Cow );
|
||||
sp->addArgs( /*MELEE*/ 0, ioid_Pig );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MobKilled.EnemyRoleId.*"); // BROKEN!
|
||||
sp->addArgs( ioid_Cow );
|
||||
sp->addArgs( ioid_Pig );
|
||||
sp->addArgs( ioid_Zombie );
|
||||
sp->addArgs( ioid_Spiderjocky );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MobKilledTotal.DifficultyLevelId.*.EnemyRoleId.*"); // BROKEN!
|
||||
sp->addArgs( 1, ioid_Cow );
|
||||
sp->addArgs( 2, ioid_Cow );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MobKilledTotal"); // BROKEN!
|
||||
sp->addArgs(-1);
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MobInteract.MobId.*.Interaction.*");
|
||||
// sp->addArgs( ioid_Cow, /*MILKED*/ 6 ); no longer an interaction type.
|
||||
sp->addArgs( ioid_Cow, /*BRED*/ 1 ); // fixed (+ach)
|
||||
sp->addArgs( ioid_Sheep, /*SHEARED*/ 5 ); // works (+ach)
|
||||
sp->addArgs( ioid_VillagerGolem, /*CRAFTED*/ 4 ); // works (+ach)
|
||||
sp->addArgs( ioid_Ozelot, /*TAMED*/ 2 ); // works (+ach)
|
||||
sp->addArgs( ioid_Zombie, /*CURED*/ 3 ); // fixed (+ach)
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"EnteredNewBiome.BiomeId.*");
|
||||
for (int i=0; i<20; i++)
|
||||
sp->addArgs( i );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"AchievementGet.AchievementId.*");
|
||||
sp->addArgs( eAward_TakingInventory ); // works (+ach)
|
||||
sp->addArgs( eAward_WhenPigsFly ); // works (+ach)
|
||||
sp->addArgs( eAward_InToTheNether ); // works (+ach)
|
||||
sp->addArgs( eAward_theEnd ); // works (+ach)
|
||||
sp->addArgs( eAward_winGame ); // fixed (+ach)
|
||||
sp->addArgs( eAward_diamondsToYou );
|
||||
sp->addArgs( eAward_stayinFrosty ); // works (achievement configured incorrectly)
|
||||
sp->addArgs( eAward_ironMan ); // fixed (+ach)
|
||||
sp->addArgs( eAward_renewableEnergy );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"TimePlayed.DifficultyLevelId.*"); // BROKEN!
|
||||
for (int i=0; i<4; i++) sp->addArgs(i); // Difficulty Levels.
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"DistanceTravelled.DifficultyLevelId.*.TravelMethodId.*");
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
sp->addArgs( i, DsTravel::eMethod_walk );
|
||||
sp->addArgs( i, DsTravel::eMethod_climb );
|
||||
sp->addArgs( i, DsTravel::eMethod_fall );
|
||||
sp->addArgs( i, DsTravel::eMethod_minecart );
|
||||
sp->addArgs( i, DsTravel::eMethod_swim );
|
||||
sp->addArgs( i, DsTravel::eMethod_pig );
|
||||
}
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"DistanceTravelled");
|
||||
sp->addArgs( /*NO ARGUMENTS*/ -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"PlayedMusicDisc.DiscId.*"); // works (+ach)
|
||||
for (int i = 2256; i < 2268; i++)
|
||||
sp->addArgs( i );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"ChestfulOfCobblestone"); // works. (+ach)
|
||||
sp->addArgs( /*NO ARGUMENTS*/ -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"Overkill"); // works. (+ach)
|
||||
sp->addArgs( /*NO ARGUMENTS*/ -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"OnARail"); // works. (+ach)
|
||||
sp->addArgs( /*NO ARGUMENTS*/ -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"Leaderboard.LeaderboardId.*.DifficultyLevelId.*");
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
sp->addArgs( eLeaderboardId_FARMING, i );
|
||||
sp->addArgs( eLeaderboardId_MINING, i );
|
||||
sp->addArgs( eLeaderboardId_TRAVELLING, i );
|
||||
sp->addArgs( eLeaderboardId_KILLING, i );
|
||||
}
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
|
||||
// Debugging 1 //
|
||||
|
||||
sp = new StatParam(L"DistanceTravelled");
|
||||
sp->addArgs( /*NO ARGUMENTS*/ -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"BlockBroken");
|
||||
sp->addArgs( /*NO ARGUMENTS*/ -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
sp = new StatParam(L"MobKilledTotal");
|
||||
sp->addArgs( /*NO ARGUMENTS*/ -1 );
|
||||
out->m_stats.push_back(sp);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void DurangoStatsDebugger::PrintStats(int iPad)
|
||||
{
|
||||
if (instance == NULL) instance = Initialize();
|
||||
|
||||
vector<wstring> *tmp = instance->getStats();
|
||||
instance->m_printQueue.insert(instance->m_printQueue.end(), tmp->begin(), tmp->end());
|
||||
|
||||
// app.DebugPrintf("[DEBUG] START\n");
|
||||
// for (wstring t : *tmp) app.DebugPrintf("[DEBUG] %s\n", wstringtofilename(t));
|
||||
// app.DebugPrintf("[DEBUG] END\n");
|
||||
|
||||
instance->retrieveStats(iPad);
|
||||
|
||||
app.DebugPrintf("[DurangoStatsDebugger] (%i) Results returned, starting printing.\n", instance->m_retrievedStats.size());
|
||||
for (StatResult result : instance->m_retrievedStats)
|
||||
{
|
||||
app.DebugPrintf("[DSB] '%s' == ", wstringtofilename(result.m_statName));
|
||||
app.DebugPrintf("%s\n", wstringtofilename(result.m_score));
|
||||
}
|
||||
|
||||
// Empty list.
|
||||
EnterCriticalSection(&instance->m_retrievedStatsLock);
|
||||
instance->m_retrievedStats.erase(
|
||||
instance->m_retrievedStats.begin(),
|
||||
instance->m_retrievedStats.end()
|
||||
);
|
||||
LeaveCriticalSection(&instance->m_retrievedStatsLock);
|
||||
|
||||
}
|
||||
|
||||
void DurangoStatsDebugger::retrieveStats(int iPad)
|
||||
{
|
||||
MXS::XboxLiveContext ^xboxLiveContext;
|
||||
try
|
||||
{
|
||||
WFC::IVectorView<Windows::Xbox::System::User^>^ userList = Windows::Xbox::System::User::Users;
|
||||
if( userList != nullptr )
|
||||
{
|
||||
for (Windows::Xbox::System::User^ user : userList)
|
||||
{
|
||||
if( user->IsSignedIn && !user->IsGuest )
|
||||
{
|
||||
xboxLiveContext = ref new MXS::XboxLiveContext(user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (xboxLiveContext == nullptr)
|
||||
{
|
||||
app.DebugPrintf("[DurangoStatsDebugger] Problem occured while creating 'XboxLiveContext'.\n"); return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Platform::Exception^ ex)
|
||||
{
|
||||
app.DebugPrintf("[DurangoStatsDebugger] Problem occured while creating 'XboxLiveContext'.\n"); return;
|
||||
}
|
||||
|
||||
if ( xboxLiveContext ) { app.DebugPrintf("[DurangoStatsDebugger] 'XboxLiveContext' created successfully.\n"); }
|
||||
else { app.DebugPrintf("[DurangoStatsDebugger] 'XboxLiveContext' not created.\n"); return; }
|
||||
|
||||
PlayerUID xuid;
|
||||
ProfileManager.GetXUID(iPad, &xuid, true);
|
||||
|
||||
const int readCount = 1, difficulty = 1, type = 0;
|
||||
|
||||
// ----------------------------------------- //
|
||||
|
||||
byte runningThreads = 0;
|
||||
byte *r_runningThreads = &runningThreads;
|
||||
|
||||
if (xuid.toString().compare(L"") == 0)
|
||||
{
|
||||
app.DebugPrintf("[DurangoStatsDebugger] NO LOGGED IN USER!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
string plrname= wstringtofilename(xuid.toString());
|
||||
app.DebugPrintf(
|
||||
"[DurangoStatsDebugger] Retrieving (%i) stats for '%s'.\n",
|
||||
m_printQueue.size(),
|
||||
plrname.c_str()
|
||||
);
|
||||
|
||||
static const unsigned short R_SIZE = 20;
|
||||
|
||||
// Create Stat retrieval threads until there is no long any stats to start retrieving.
|
||||
while ( !instance->m_printQueue.empty() )
|
||||
{
|
||||
vector<wstring> *printing = new vector<wstring>();
|
||||
|
||||
if (m_printQueue.size() > R_SIZE)
|
||||
{
|
||||
printing->insert( printing->end(), m_printQueue.begin(), m_printQueue.begin() + R_SIZE );
|
||||
m_printQueue.erase( m_printQueue.begin(), m_printQueue.begin() + R_SIZE );
|
||||
}
|
||||
else
|
||||
{
|
||||
printing->insert( printing->end(), m_printQueue.begin(), m_printQueue.end() );
|
||||
m_printQueue.erase( m_printQueue.begin(), m_printQueue.end() );
|
||||
}
|
||||
|
||||
// ------------------------------------------ //
|
||||
|
||||
app.DebugPrintf("[DurangoStatsDebugger] Starting retrieval operation (%i/%i stats).\n", printing->size(), R_SIZE);
|
||||
|
||||
runningThreads++;
|
||||
|
||||
// Fill statNames string.
|
||||
PC::Vector<P::String^>^ statNames = ref new PC::Vector<P::String^>();
|
||||
for ( auto it = printing->begin(); it != printing->end(); it++ )
|
||||
statNames->Append( ref new P::String(it->c_str()) );
|
||||
|
||||
// Create vector of the XboxId we want.
|
||||
PC::Vector<P::String^>^ xboxUserIds = ref new PC::Vector<P::String^>();
|
||||
xboxUserIds->Append(ref new P::String(xuid.toString().c_str()));
|
||||
|
||||
auto asyncOp = xboxLiveContext->UserStatisticsService->GetMultipleUserStatisticsAsync(
|
||||
xboxUserIds->GetView(), // the collection of Xbox user IDs whose stats we want to retrieve
|
||||
SERVICE_CONFIG_ID, // the service config that contains the stats we want
|
||||
statNames->GetView() // a list of stat names we want
|
||||
);
|
||||
|
||||
CC::create_task(asyncOp)
|
||||
.then( [this,printing,iPad,r_runningThreads] (CC::task<WFC::IVectorView<MXS::UserStatistics::UserStatisticsResult^>^> resultListTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
WFC::IVectorView<MXS::UserStatistics::UserStatisticsResult^>^ resultList = resultListTask.get();
|
||||
int userIndex = 0;
|
||||
for( MXS::UserStatistics::UserStatisticsResult^ result : resultList )
|
||||
{
|
||||
for( UINT index = 0; index<result->ServiceConfigurationStatistics->Size; index++ )
|
||||
{
|
||||
MXS::UserStatistics::ServiceConfigurationStatistic^ configStat = result->ServiceConfigurationStatistics->GetAt(index);
|
||||
|
||||
app.DebugPrintf("[DurangoStatsDebugger] Retrieve complete, %i results returned.\n", configStat->Statistics->Size);
|
||||
|
||||
for (auto result : configStat->Statistics)
|
||||
{
|
||||
StatResult sr = { iPad, result->StatisticName->Data(), result->Value->Data() };
|
||||
this->addRetrievedStat(sr);
|
||||
}
|
||||
}
|
||||
++userIndex;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (Platform::Exception ^ex)
|
||||
{
|
||||
app.DebugPrintf("[DurangoStatsDebugger] resultListTask.get() encountered an exception:\n\t'%s'\n", ex->ToString()->Data() );
|
||||
}
|
||||
|
||||
(*r_runningThreads)--;
|
||||
});
|
||||
}
|
||||
|
||||
while (runningThreads > 0) Sleep(5);
|
||||
}
|
||||
|
||||
void DurangoStatsDebugger::addRetrievedStat(StatResult result)
|
||||
{
|
||||
EnterCriticalSection(&m_retrievedStatsLock);
|
||||
m_retrievedStats.push_back(result);
|
||||
LeaveCriticalSection(&m_retrievedStatsLock);
|
||||
}
|
||||
94
Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.h
Normal file
94
Minecraft.Client/Durango/Leaderboards/DurangoStatsDebugger.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
namespace P = Platform;
|
||||
namespace PC = Platform::Collections;
|
||||
namespace WF = Windows::Foundation;
|
||||
namespace WFC = Windows::Foundation::Collections;
|
||||
namespace MXSL = Microsoft::Xbox::Services::Leaderboard;
|
||||
|
||||
class StatParam
|
||||
{
|
||||
private:
|
||||
wstring m_base;
|
||||
int m_numArgs;
|
||||
|
||||
vector<int> m_args;
|
||||
|
||||
public:
|
||||
StatParam(const wstring &base);
|
||||
|
||||
void addArgs(int v1, ...);
|
||||
|
||||
vector<wstring> *getStats();
|
||||
|
||||
};
|
||||
|
||||
|
||||
class DurangoStatsDebugger
|
||||
{
|
||||
protected:
|
||||
static DurangoStatsDebugger *instance;
|
||||
|
||||
enum
|
||||
{
|
||||
ioid_Arrow = 10,
|
||||
|
||||
ioid_Spiderjocky = 49,
|
||||
ioid_Creeper = 50,
|
||||
ioid_Skeleton,
|
||||
ioid_Spider,
|
||||
ioid_Giant,
|
||||
ioid_Zombie,
|
||||
ioid_Slime,
|
||||
ioid_Ghast,
|
||||
ioid_PigZombie,
|
||||
ioid_Enderman,
|
||||
ioid_CaveSpider,
|
||||
ioid_Silverfish,
|
||||
ioid_Blaze,
|
||||
ioid_LavaSlime,
|
||||
ioid_EnderDragon,
|
||||
|
||||
ioid_Pig = 90,
|
||||
ioid_Sheep,
|
||||
ioid_Cow,
|
||||
ioid_Chicken,
|
||||
ioid_Squid,
|
||||
ioid_Wolf,
|
||||
ioid_MushroomCow,
|
||||
ioid_SnowMan,
|
||||
ioid_Ozelot,
|
||||
ioid_VillagerGolem,
|
||||
};
|
||||
|
||||
DurangoStatsDebugger();
|
||||
|
||||
vector<StatParam *> m_stats;
|
||||
|
||||
vector<wstring> *getStats();
|
||||
|
||||
public:
|
||||
static DurangoStatsDebugger *Initialize();
|
||||
|
||||
static void PrintStats(int iPad);
|
||||
|
||||
private:
|
||||
vector<wstring> m_printQueue;
|
||||
|
||||
void retrieveStats(int iPad);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int m_iPad;
|
||||
wstring m_statName;
|
||||
wstring m_score;
|
||||
} StatResult;
|
||||
|
||||
CRITICAL_SECTION m_retrievedStatsLock;
|
||||
|
||||
vector<StatResult> m_retrievedStats;
|
||||
|
||||
void addRetrievedStat(StatResult result);
|
||||
};
|
||||
107
Minecraft.Client/Durango/Leaderboards/GameProgress.cpp
Normal file
107
Minecraft.Client/Durango/Leaderboards/GameProgress.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Durango\ServiceConfig\Events-XBLA.8-149E11AEEvents.h"
|
||||
|
||||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
|
||||
#include "GameProgress.h"
|
||||
|
||||
namespace WFC = Windows::Foundation::Collections;
|
||||
namespace MXSA = Microsoft::Xbox::Services::Achievements;
|
||||
namespace CC = concurrency;
|
||||
|
||||
GameProgress *GameProgress::instance = NULL;
|
||||
|
||||
void GameProgress::Flush(int iPad)
|
||||
{
|
||||
if (instance == NULL)
|
||||
instance = new GameProgress();
|
||||
|
||||
instance->updatePlayer(iPad);
|
||||
}
|
||||
|
||||
void GameProgress::Tick()
|
||||
{
|
||||
if (instance == NULL)
|
||||
instance = new GameProgress();
|
||||
|
||||
long long currentTime = System::currentTimeMillis();
|
||||
if ( (currentTime - instance->m_lastUpdate) > (UPDATE_FREQUENCY / 4) )
|
||||
{
|
||||
instance->updatePlayer(instance->m_nextPad);
|
||||
instance->m_nextPad = ++instance->m_nextPad % MAX_LOCAL_PLAYERS;
|
||||
instance->m_lastUpdate = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
GameProgress::GameProgress()
|
||||
{
|
||||
m_nextPad = 0;
|
||||
m_lastUpdate = 0;
|
||||
}
|
||||
|
||||
void GameProgress::updatePlayer(int iPad)
|
||||
{
|
||||
if ( ProfileManager.IsGuest(iPad) || !ProfileManager.IsSignedInLive(iPad) ) return;
|
||||
|
||||
PlayerUID uid;
|
||||
ProfileManager.GetXUID(iPad, &uid, true);
|
||||
|
||||
WXS::User^ user = ProfileManager.GetUser(iPad);
|
||||
|
||||
if (user == nullptr) return;
|
||||
|
||||
MXS::XboxLiveContext ^xlc = ref new MXS::XboxLiveContext(user);
|
||||
|
||||
// Get these while they are still valid.
|
||||
LPCGUID playerSession = DurangoStats::getPlayerSession();
|
||||
|
||||
CC::create_task(
|
||||
xlc->AchievementService->GetAchievementsForTitleIdAsync(
|
||||
ref new Platform::String(uid.toString().c_str()), // Xuid
|
||||
0x149E11AE, // TitleId
|
||||
MXSA::AchievementType::Persistent, // Use regular achievements (not challenges)
|
||||
false, // Unlocked only
|
||||
MXSA::AchievementOrderBy::UnlockTime, // Order (we don't really care)
|
||||
0, // skipItems (start index)
|
||||
200 // MaxItems
|
||||
)
|
||||
).then( [this,iPad,uid,playerSession] (CC::task<MXSA::AchievementsResult^> resultTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
int achievementsUnlocked = 0;
|
||||
|
||||
MXSA::AchievementsResult^ result = resultTask.get();
|
||||
if(result)
|
||||
{
|
||||
for (unsigned int i = 0, iMax = result->Items->Size; i < iMax; i++)
|
||||
{
|
||||
MXSA::Achievement^ ach = result->Items->GetAt(i);
|
||||
if (ach->ProgressState == MXSA::AchievementProgressState::Achieved)
|
||||
achievementsUnlocked++;
|
||||
}
|
||||
|
||||
float gameprogress;
|
||||
if (EventWriteGameProgress(
|
||||
uid.toString().c_str(),
|
||||
playerSession,
|
||||
gameprogress = calcGameProgress(achievementsUnlocked) )
|
||||
== 0)
|
||||
{
|
||||
app.DebugPrintf("<%ls> GameProgress(%.1f)\n", uid.toString().c_str(), gameprogress);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Platform::Exception ^ex)
|
||||
{
|
||||
app.DebugPrintf("GameProgress:: Error, couldn't contact the achievments service (?): %ls", ex->Message->Data());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
float GameProgress::calcGameProgress(int achievementsUnlocked)
|
||||
{
|
||||
return (float) achievementsUnlocked / 0.60f;
|
||||
}
|
||||
23
Minecraft.Client/Durango/Leaderboards/GameProgress.h
Normal file
23
Minecraft.Client/Durango/Leaderboards/GameProgress.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
class GameProgress
|
||||
{
|
||||
private:
|
||||
static GameProgress *instance;
|
||||
|
||||
public:
|
||||
static const long long UPDATE_FREQUENCY = 64 * 1000;
|
||||
|
||||
static void Tick();
|
||||
static void Flush(int iPad);
|
||||
|
||||
protected:
|
||||
GameProgress();
|
||||
|
||||
int m_nextPad;
|
||||
long long m_lastUpdate;
|
||||
|
||||
void updatePlayer(int iPad);
|
||||
|
||||
float calcGameProgress(int achievementsUnlocked);
|
||||
};
|
||||
Reference in New Issue
Block a user