the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "DurangoLeaderboardManager.h"
3#include "..\..\..\Minecraft.World\StringHelpers.h"
4
5namespace WFC = Windows::Foundation::Collections;
6namespace CC = concurrency;
7
8LeaderboardManager *LeaderboardManager::m_instance = new DurangoLeaderboardManager(); //Singleton instance of the LeaderboardManager
9
10DurangoLeaderboardManager::DurangoLeaderboardManager()
11{
12 m_eStatsState = eStatsState_Idle;
13 InitializeCriticalSection(&m_csStatsState);
14
15 m_openSessions = 0;
16 m_xboxLiveContext = nullptr;
17 m_scores = NULL;
18 m_readCount = 0;
19 m_maxRank = 0;
20 m_waitingForProfiles = false;
21
22 m_difficulty = 0;
23 m_type = eStatsType_UNDEFINED;
24 m_statNames = ref new PC::Vector<P::String^>();
25 m_xboxUserIds = ref new PC::Vector<P::String^>();
26 m_leaderboardAsyncOp = nullptr;
27 m_statsAsyncOp = nullptr;
28
29 for(unsigned int difficulty = 0; difficulty < 4; ++difficulty)
30 {
31 m_leaderboardNames[difficulty][eStatsType_Travelling] = L"LeaderboardTravelling" + _toString(difficulty);
32 m_leaderboardNames[difficulty][eStatsType_Mining] = L"LeaderboardMining" + _toString(difficulty);
33 m_leaderboardNames[difficulty][eStatsType_Farming] = L"LeaderboardFarming" + _toString(difficulty);
34 m_leaderboardNames[difficulty][eStatsType_Kills] = L"LeaderboardKills" + _toString(difficulty);
35
36 m_socialLeaderboardNames[difficulty][eStatsType_Travelling] = L"Leaderboard.LeaderboardId.0.DifficultyLevelId." + _toString(difficulty);
37 m_socialLeaderboardNames[difficulty][eStatsType_Mining] = L"Leaderboard.LeaderboardId.1.DifficultyLevelId." + _toString(difficulty);
38 m_socialLeaderboardNames[difficulty][eStatsType_Farming] = L"Leaderboard.LeaderboardId.2.DifficultyLevelId." + _toString(difficulty);
39 m_socialLeaderboardNames[difficulty][eStatsType_Kills] = L"Leaderboard.LeaderboardId.3.DifficultyLevelId." + _toString(difficulty);
40
41 m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.0"); // Walked
42 m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.2"); // Fallen
43 m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.4"); // Minecart
44 m_leaderboardStatNames[difficulty][eStatsType_Travelling].push_back( L"DistanceTravelled.DifficultyLevelId." + _toString(difficulty) + L".TravelMethodId.5"); // Boat
45
46 m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.3"); // Dirt
47 m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.4"); // Cobblestone
48 m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.12"); // Sand
49 m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.1"); // Stone
50 m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.13"); // Gravel
51 m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.82"); // Clay
52 m_leaderboardStatNames[difficulty][eStatsType_Mining].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.49"); // Obsidian
53
54 m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.1.ItemId.344"); // Eggs
55 m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.59"); // Wheat
56 m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.39"); // Mushroom
57 m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"BlockBroken.DifficultyLevelId." + _toString(difficulty) + L".BlockId.83"); // Sugarcane
58 m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.2.ItemId.335"); // Milk
59 m_leaderboardStatNames[difficulty][eStatsType_Farming].push_back( L"McItemAcquired.DifficultyLevelId." + _toString(difficulty) + L".AcquisitionMethodId.1.ItemId.86"); // Pumpkin
60
61 m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.54"); // Zombie
62 m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.51"); // Skeleton
63 m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.50"); // Creeper
64 m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.52"); // Spider
65 m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.49"); // Spider Jockey
66 m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.57"); // Zombie Pigman
67 m_leaderboardStatNames[difficulty][eStatsType_Kills].push_back( L"MobKilledTotal.DifficultyLevelId." + _toString(difficulty) + L".EnemyRoleId.55"); // Slime
68 }
69}
70
71void DurangoLeaderboardManager::Tick()
72{
73 ReadView view;
74
75 switch( getState() )
76 {
77 case eStatsState_GettingLeaderboardInfo:
78 break;
79 case eStatsState_ReceivedLeaderboardInfo:
80 {
81 setState(eStatsState_GettingStatsInfo);
82
83 // Get the actual display info for the stats
84 m_statsAsyncOp = m_xboxLiveContext->UserStatisticsService->GetMultipleUserStatisticsAsync(
85 m_xboxUserIds->GetView(), // the collection of Xbox user IDs whose stats we want to retrieve
86 SERVICE_CONFIG_ID, // the service config that contains the stats we want
87 m_statNames->GetView() // a list of stat names we want
88 );
89
90 auto task = concurrency::create_task(m_statsAsyncOp).then( [this] (CC::task<WFC::IVectorView<MXS::UserStatistics::UserStatisticsResult^>^> resultListTask)
91 {
92 try
93 {
94 app.DebugPrintf("[LeaderboardManager] Second continuation\n");
95 m_statsAsyncOp = nullptr;
96
97 WFC::IVectorView<MXS::UserStatistics::UserStatisticsResult^>^ resultList = resultListTask.get();
98
99 if (m_xboxLiveContext == nullptr) throw(ref new P::Exception(-1));
100
101 int userIndex = 0;
102 for( MXS::UserStatistics::UserStatisticsResult^ result : resultList )
103 {
104 app.DebugPrintf("XboxUserId: %ls\n", result->XboxUserId->Data());
105
106 for( UINT index = 0; index<result->ServiceConfigurationStatistics->Size; index++ )
107 {
108 MXS::UserStatistics::ServiceConfigurationStatistic^ configStat = result->ServiceConfigurationStatistics->GetAt(index);
109 //app.DebugPrintf("ServiceConfigurationId: %ls\n", configStat->ServiceConfigurationId->Data());
110
111 updateStatsInfo(userIndex, m_difficulty, m_type, configStat->Statistics);
112 }
113 ++userIndex;
114 }
115
116 app.DebugPrintf("[LeaderboardManager] Setting to ready\n");
117 setState(eStatsState_Ready);
118
119 }
120 catch (Platform::Exception^ ex)
121 {
122 m_leaderboardAsyncOp = nullptr;
123 setState(eStatsState_Failed);
124
125 if (ex->HResult == HTTP_E_STATUS_NOT_FOUND) app.DebugPrintf("[LeaderboardManager] ERROR calling GetLeaderboardAsync: 404 Not Found - 0x%0.8x\n", ex->HResult);
126 else app.DebugPrintf("[LeaderboardManager] ERROR calling GetLeaderboardAsync: 0x%0.8x\n", ex->HResult);
127 }
128 catch (...)
129 {
130 app.DebugPrintf("[LeaderboardManager] SecondContinuation: Unknown exception.\n");
131 }
132 });
133 }
134 break;
135 case eStatsState_GettingStatsInfo:
136 break;
137 case eStatsState_Ready:
138 {
139 // If we're waiting on profiles, don't return scores just yet
140 if (m_waitingForProfiles)
141 {
142 return;
143 }
144 else
145 {
146 if (m_displayNames.size() == m_readCount)
147 {
148 // Add display names to scores
149 for (int i = 0; i < m_displayNames.size(); i++)
150 {
151 m_scores[i].m_name = m_displayNames[i];
152 }
153 }
154 else
155 {
156 // This seem to happen if something went wrong with Xbox Live
157 app.DebugPrintf("DurangoLeaderboardManager::Tick: Display names count (%i) didn't match read count (%i)", m_displayNames.size(), m_readCount);
158 }
159
160 m_displayNames.clear();
161 }
162
163 //assert(m_scores != NULL || m_readCount == 0);
164
165 view.m_numQueries = m_readCount;
166 view.m_queries = m_scores;
167
168 // 4J-JEV: Debugging.
169 //LeaderboardManager::printStats(view);
170
171 eStatsReturn ret = view.m_numQueries > 0 ? eStatsReturn_Success : eStatsReturn_NoResults;
172
173 if (m_readListener != NULL)
174 {
175 app.DebugPrintf("[LeaderboardManager] OnStatsReadComplete(%i, %i, _)\n", ret, m_readCount);
176 m_readListener->OnStatsReadComplete(ret, m_maxRank, view);
177 }
178
179 app.DebugPrintf("[LeaderboardManager] Deleting scores\n");
180 delete [] m_scores;
181 m_scores = NULL;
182
183 setState(eStatsState_Idle);
184 }
185 break;
186
187 case eStatsState_Failed:
188 view.m_numQueries = 0;
189 view.m_queries = NULL;
190
191 if ( m_readListener != NULL )
192 {
193 m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, 0, view);
194 }
195
196 setState(eStatsState_Idle);
197
198 break;
199
200 case eStatsState_Canceled:
201 app.DebugPrintf("[LeaderboardManager] Setting canceled\n");
202 setState(eStatsState_Idle);
203 break;
204
205 default: // Getting or Idle.
206 if (m_openSessions == 0 && m_xboxLiveContext != nullptr)
207 {
208 m_xboxLiveContext = nullptr;
209
210 app.DebugPrintf("[LeaderboardManager] Tick(): Nulled XboxLiveContext\n");
211 }
212 break;
213 }
214}
215
216//Open a session
217bool DurangoLeaderboardManager::OpenSession()
218{
219 if (m_openSessions == 0)
220 {
221 app.DebugPrintf("[LeaderboardManager] OpenSession()\n");
222
223 try
224 {
225 WXS::User^ user = ProfileManager.GetUser(ProfileManager.GetPrimaryPad());
226 if(user != nullptr && user->IsSignedIn && !user->IsGuest)
227 {
228 m_xboxLiveContext = ref new MXS::XboxLiveContext(user);
229 }
230 else
231 {
232 app.DebugPrintf("[LeaderboardManager] OpenSession(): Failed to created new XboxLiveContext, No valid user\n");
233 return false;
234 }
235 }
236 catch (Platform::Exception^ ex)
237 {
238 m_xboxLiveContext = nullptr;
239 app.DebugPrintf("[LeaderboardManager] OpenSession(): Failed to created new XboxLiveContext, ret == 0x%08X.\n", ex->HResult);
240 return false;
241 }
242 catch (...)
243 {
244 app.DebugPrintf("[LeaderboardManager] OpenSession(): Unknown exception.\n");
245 return false;
246 }
247 }
248 else app.DebugPrintf("[LeaderboardManager] OpenSession(): Another session opened, total=%i\n", m_openSessions+1);
249
250 m_openSessions++;
251 return true;
252}
253
254//Close a session
255void DurangoLeaderboardManager::CloseSession()
256{
257 m_openSessions--;
258
259 if (m_openSessions == 0)
260 {
261 if(isIdle())
262 {
263 m_xboxLiveContext = nullptr;
264
265 app.DebugPrintf("[LeaderboardManager] CloseSession(): Nulled XboxLiveContext\n");
266 }
267 }
268 else app.DebugPrintf("[LeaderboardManager] CloseSession(): %i sessions still open.\n", m_openSessions);
269}
270
271//Delete a session
272void DurangoLeaderboardManager::DeleteSession()
273{
274}
275
276//Write the given stats
277//This is called synchronously and will not free any memory allocated for views when it is done
278
279bool DurangoLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views)
280{
281 return false;
282}
283
284bool DurangoLeaderboardManager::ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount)
285{
286 // Need to cancel read/write operation first.
287 if (!isIdle()) return false;
288 if (!LeaderboardManager::ReadStats_Friends(listener, difficulty, type, myUID, startIndex, readCount)) return false;
289 setState(eStatsState_GettingLeaderboardInfo);
290
291 if( m_xboxLiveContext == nullptr )
292 {
293 throw ref new Platform::InvalidArgumentException(L"A valid User is required");
294 }
295
296 // Request the leaderboard to get ranking information
297 auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardForSocialGroupWithSkipToRankAsync(
298 ref new P::String(myUID.toString().c_str()),
299 SERVICE_CONFIG_ID,
300 ref new P::String(m_socialLeaderboardNames[difficulty][type].c_str()),
301 MXS::Social::SocialGroupConstants::People,
302 startIndex,
303 ref new P::String(L"descending"),
304 readCount
305 );
306
307 runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_Friends);
308
309 return true;
310}
311
312bool DurangoLeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount)
313{
314 // Need to cancel read/write operation first.
315 if (!isIdle()) return false;
316 if (!LeaderboardManager::ReadStats_MyScore(listener, difficulty, type, myUID, readCount)) return false;
317 setState(eStatsState_GettingLeaderboardInfo);
318
319 if( m_xboxLiveContext == nullptr )
320 {
321 throw ref new Platform::InvalidArgumentException(L"A valid User is required");
322 }
323
324 P::String^ leaderboardName = ref new P::String(m_leaderboardNames[difficulty][type].c_str());
325
326 // Request the leaderboard to get ranking information
327 auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardWithSkipToUserAsync(
328 SERVICE_CONFIG_ID,
329 leaderboardName,
330 ref new P::String(myUID.toString().c_str()), // skip to this user
331 readCount
332 );
333
334 runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_MyScore);
335
336 return true;
337}
338
339bool DurangoLeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount)
340{
341 // Need to cancel read/write operation first.
342 if (!isIdle()) return false;
343 if (!LeaderboardManager::ReadStats_TopRank(listener, difficulty, type, startIndex, readCount)) return false;
344 setState(eStatsState_GettingLeaderboardInfo);
345
346 if( m_xboxLiveContext == nullptr )
347 {
348 throw ref new Platform::InvalidArgumentException(L"A valid User is required");
349 }
350
351 P::String^ leaderboardName = ref new P::String(m_leaderboardNames[difficulty][type].c_str());
352
353 // Request the leaderboard to get ranking information
354 auto asyncOp = m_xboxLiveContext->LeaderboardService->GetLeaderboardAsync(
355 SERVICE_CONFIG_ID,
356 leaderboardName,
357 startIndex, // skip this many ranks
358 readCount
359 );
360
361 runLeaderboardRequest(asyncOp, difficulty, type, readCount, EFilterMode::eFM_TopRank);
362
363 return true;
364}
365
366//Perform a flush of the stats
367void DurangoLeaderboardManager::FlushStats()
368{
369}
370
371//Cancel the current operation
372void DurangoLeaderboardManager::CancelOperation()
373{
374 m_readListener = NULL;
375 setState(eStatsState_Canceled);
376
377 if(m_leaderboardAsyncOp) m_leaderboardAsyncOp->Cancel();
378 if(m_statsAsyncOp) m_statsAsyncOp->Cancel();
379
380 //if (m_transactionCtx != 0)
381 //{
382 // int ret = sceNpScoreAbortTransaction(m_transactionCtx);
383 //
384 // if (ret < 0)
385 // {
386 // app.DebugPrintf("[LeaderboardManager] CancelOperation(): Problem encountered aborting current operation, 0x%X.\n", ret);
387 // }
388 // else
389 // {
390 // app.DebugPrintf("[LeaderboardManager] CancelOperation(): Operation aborted successfully.\n");
391 // }
392 //}
393 //else app.DebugPrintf("[LeaderboardManager] CancelOperation(): No current operation.\n");
394}
395
396//Is the leaderboard manager idle.
397bool DurangoLeaderboardManager::isIdle()
398{
399 return getState() == eStatsState_Idle;
400}
401
402void DurangoLeaderboardManager::runLeaderboardRequest(WF::IAsyncOperation<MXSL::LeaderboardResult^>^ asyncOp, int difficulty, EStatsType type, unsigned int readCount, EFilterMode filter)
403{
404 m_leaderboardAsyncOp = asyncOp;
405 m_difficulty = difficulty;
406 m_type = type;
407
408 // Build stat names
409 m_statNames = ref new PC::Vector<P::String^>();
410 m_statNames->Clear();
411 for(wstring name : m_leaderboardStatNames[difficulty][type])
412 {
413 m_statNames->Append(ref new P::String(name.c_str()));
414 }
415
416 app.DebugPrintf("[LeaderboardManager] Running request\n");
417 CC::create_task(asyncOp)
418 .then( [this, readCount, difficulty, type, filter] (CC::task<MXSL::LeaderboardResult^> resultTask)
419 {
420 try
421 {
422 app.DebugPrintf("[LeaderboardManager] First continuation.\n");
423
424 m_leaderboardAsyncOp = nullptr;
425
426 MXSL::LeaderboardResult^ lastResult = resultTask.get();
427
428 app.DebugPrintf(
429 "Name: %ls - Filter: %ls - Rows: Retrieved=%d Total=%d Requested=%d.\n",
430 lastResult->DisplayName->Data(),
431 LeaderboardManager::filterNames[ (int) filter ].c_str(),
432 lastResult->Rows->Size, lastResult->TotalRowCount, readCount
433 );
434
435 //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);
436
437 // 4J-JEV: Fix for Xbox One #162541 - [CRASH] Code: Leaderboards: Title crashes after pressing [B] Back button while changing Leaderboards' filter to 'My Score'
438 // 4J-JEV: Fix for X1: #165487 - [CRASH] XR-074: Compliance: The title does not properly handle switching to offline session while browsing the Leaderboards.
439 if (m_xboxLiveContext == nullptr) throw(ref new P::Exception(-1));
440
441 // If this is My_Score, check that the user appears
442 if (filter == eFM_MyScore)
443 {
444 bool userIncluded = false;
445 for(int i = 0; i < lastResult->Rows->Size; i++)
446 {
447 if (lastResult->Rows->GetAt(i)->XboxUserId == m_xboxLiveContext->User->XboxUserId) userIncluded = true;
448 }
449
450 // If the user isn't included, don't show the results
451 if (!userIncluded)
452 {
453 m_readCount = 0;
454 throw(ref new P::Exception(INET_E_DATA_NOT_AVAILABLE));
455 }
456 }
457
458 m_maxRank = lastResult->TotalRowCount;
459 m_readCount = lastResult->Rows->Size;
460
461 if (m_scores != NULL) delete [] m_scores;
462 m_scores = new ReadScore[m_readCount];
463 ZeroMemory(m_scores, sizeof(ReadScore) * m_readCount);
464
465 m_xboxUserIds->Clear();
466
467 app.DebugPrintf("[LeaderboardManager] Retrieved Scores:\n");
468 for( UINT index = 0; index < lastResult->Rows->Size; index++ )
469 {
470 MXSL::LeaderboardRow^ row = lastResult->Rows->GetAt(index);
471
472 app.DebugPrintf(
473 "\tIndex: %d\tRank: %d\tPercentile: %.1f%%\tXboxUserId: %ls\tValue: %ls.\n",
474 index,
475 row->Rank,
476 row->Percentile * 100,
477 row->XboxUserId->Data(),
478 row->Values->GetAt(0)->Data()
479 );
480
481 m_scores[index].m_name = row->Gamertag->Data();
482 m_scores[index].m_rank = row->Rank;
483 m_scores[index].m_uid = PlayerUID(row->XboxUserId->Data());
484
485 // 4J-JEV: Added to help determine if this player's score is hidden due to their privacy settings.
486 m_scores[index].m_totalScore = (unsigned long) _fromString<long long>(row->Values->GetAt(0)->Data());
487
488 m_xboxUserIds->Append(row->XboxUserId);
489 }
490
491 if(m_readCount > 0)
492 {
493 vector<PlayerUID> xuids = vector<PlayerUID>();
494 for(int i = 0; i < lastResult->Rows->Size; i++)
495 {
496 xuids.push_back(PlayerUID(lastResult->Rows->GetAt(i)->XboxUserId->Data()));
497 }
498 m_waitingForProfiles = true;
499 ProfileManager.GetProfiles(xuids, &GetProfilesCallback, this);
500 setState(eStatsState_ReceivedLeaderboardInfo);
501 }
502 else
503 {
504 // we hit the end of the list
505 app.DebugPrintf("Reach the end\n");
506 setState(eStatsState_Ready);
507 }
508 }
509 catch (Platform::Exception^ ex)
510 {
511 m_leaderboardAsyncOp = nullptr;
512 if (ex->HResult == INET_E_DATA_NOT_AVAILABLE)
513 {
514 // we hit the end of the list
515 app.DebugPrintf("ERROR: Reach the end\n");
516 setState(eStatsState_Ready);
517 }
518 else if(ex->HResult == HTTP_E_STATUS_NOT_FOUND)
519 {
520 app.DebugPrintf("Error calling GetLeaderboardAsync function: 404 Not Found - 0x%0.8x\n", ex->HResult);
521 setState(eStatsState_Failed);
522 }
523 else
524 {
525 app.DebugPrintf("Error calling GetLeaderboardAsync function: 0x%0.8x\n", ex->HResult);
526 setState(eStatsState_Failed);
527 }
528 }
529 catch (...)
530 {
531 app.DebugPrintf("[LeaderboardManager] Unknown exception.\n");
532 }
533 });
534}
535
536void DurangoLeaderboardManager::updateStatsInfo(int userIndex, int difficulty, EStatsType type, WFC::IVectorView<MXS::UserStatistics::Statistic^>^ statsResult)
537{
538 if (m_scores)
539 {
540 LeaderboardManager::ReadScore *userScore = &m_scores[userIndex];
541
542 switch (type)
543 {
544 case eStatsType_Farming: userScore->m_statsSize = 6; break;
545 case eStatsType_Mining: userScore->m_statsSize = 7; break;
546 case eStatsType_Kills: userScore->m_statsSize = 7; break;
547 case eStatsType_Travelling: userScore->m_statsSize = 4; break;
548 }
549
550 int statIndex = 0, sumScores = 0;
551 for(wstring statName : m_leaderboardStatNames[difficulty][type])
552 {
553 bool found = false;
554 for(auto result : statsResult)
555 {
556 if(statName.compare(result->StatisticName->Data()) == 0)
557 {
558 userScore->m_statsData[statIndex] = _fromString<unsigned long>(result->Value->Data());
559 found = true;
560 break;
561 }
562 }
563 if(!found)
564 {
565 userScore->m_statsData[statIndex] = 0;
566 }
567
568 sumScores += userScore->m_statsData[statIndex];
569 ++statIndex;
570 }
571
572 if ( (sumScores == 0) && (userScore->m_totalScore > 0) )
573 {
574 app.DebugPrintf("[LeaderboardManager] Player '%s' (rank %d) likely has privacy settings enabled.\n", userScore->m_name.c_str(), userScore->m_rank);
575 userScore->m_idsErrorMessage = IDS_LEADERBOARD_SCORE_HIDDEN;
576 }
577 }
578}
579
580void DurangoLeaderboardManager::GetProfilesCallback(LPVOID param, std::vector<Microsoft::Xbox::Services::Social::XboxUserProfile^> profiles)
581{
582 DurangoLeaderboardManager *dlm = (DurangoLeaderboardManager *)param;
583
584 app.DebugPrintf("[LeaderboardManager] GetProfilesCallback, profiles.size() == %d.\n", profiles.size());
585
586 if (profiles.size() > 0)
587 {
588 dlm->m_displayNames = vector<wstring>();
589 for (int i = 0; i < profiles.size(); i++)
590 {
591 dlm->m_displayNames.push_back(profiles[i]->GameDisplayName->Data());
592 }
593 }
594 else
595 {
596 dlm->setState(eStatsState_Failed);
597 }
598
599 dlm->m_waitingForProfiles = false;
600}
601
602DurangoLeaderboardManager::EStatsState DurangoLeaderboardManager::getState()
603{
604 return m_eStatsState;
605}
606
607void DurangoLeaderboardManager::setState(EStatsState newState)
608{
609 EnterCriticalSection(&m_csStatsState);
610
611 bool validTransition = false;
612
613 switch(m_eStatsState)
614 {
615 case eStatsState_Idle:
616 switch(newState)
617 {
618 case eStatsState_GettingLeaderboardInfo:
619 validTransition = true;
620 break;
621 };
622 break;
623 case eStatsState_GettingLeaderboardInfo:
624 switch(newState)
625 {
626 case eStatsState_Ready:
627 case eStatsState_ReceivedLeaderboardInfo:
628 case eStatsState_Canceled:
629 case eStatsState_Failed:
630 validTransition = true;
631 break;
632 };
633 break;
634 break;
635 case eStatsState_ReceivedLeaderboardInfo:
636 switch(newState)
637 {
638 case eStatsState_GettingStatsInfo:
639 case eStatsState_Canceled:
640 case eStatsState_Failed:
641 validTransition = true;
642 break;
643 };
644 break;
645 case eStatsState_GettingStatsInfo:
646 switch(newState)
647 {
648 case eStatsState_Ready:
649 case eStatsState_Canceled:
650 case eStatsState_Failed:
651 validTransition = true;
652 break;
653 };
654 break;
655 case eStatsState_Failed:
656 switch(newState)
657 {
658 case eStatsState_Idle:
659 validTransition = true;
660 break;
661 };
662 break;
663 case eStatsState_Ready:
664 switch(newState)
665 {
666 case eStatsState_Canceled:
667 case eStatsState_Idle:
668 case eStatsState_Failed:
669 validTransition = true;
670 break;
671 };
672 break;
673 case eStatsState_Canceled:
674 switch(newState)
675 {
676 case eStatsState_Ready:
677 newState = eStatsState_Idle;
678 case eStatsState_Idle:
679 validTransition = true;
680 break;
681 };
682 break;
683 };
684
685#ifndef _CONTENT_PACKAGE
686 app.DebugPrintf(
687 "[LeaderboardManager] %s state transition:\t%ls(%d) -> %ls(%d).\n",
688 (validTransition ? "Valid" : "INVALID"),
689 stateToString(m_eStatsState).c_str(), m_eStatsState,
690 stateToString(newState).c_str(), newState
691 );
692#endif
693
694 if (validTransition)
695 {
696 m_eStatsState = newState;
697 }
698
699 LeaveCriticalSection(&m_csStatsState);
700}
701
702wstring DurangoLeaderboardManager::stateToString(EStatsState eState)
703{
704 switch (eState)
705 {
706 case eStatsState_Idle: return L"eStatsState_Idle";
707 case eStatsState_GettingLeaderboardInfo: return L"eStatsState_GettingLeaderboardInfo";
708 case eStatsState_ReceivedLeaderboardInfo: return L"eStatsState_ReceivedLeaderboardInfo";
709 case eStatsState_GettingStatsInfo: return L"eStatsState_GettingStatsInfo";
710 case eStatsState_ReceivedStatsInfo: return L"eStatsState_ReceivedStatsInfo";
711 case eStatsState_Failed: return L"eStatsState_Failed";
712 case eStatsState_Ready: return L"eStatsState_Ready";
713 case eStatsState_Canceled: return L"eStatsState_Canceled";
714 case eStatsState_Max: return L"eStatsState_MAX";
715 default: return L"UNKNOWN";
716 }
717}