the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2
3#include "OrbisLeaderboardManager.h"
4
5#include "base64.h"
6
7#include "..\Orbis_App.h"
8#include "..\..\Common\Consoles_App.h"
9
10#include "Common\Network\Sony\SQRNetworkManager.h"
11
12#include "..\..\..\Minecraft.World\StringHelpers.h"
13
14#include <cstdlib>
15
16#include <np.h>
17//#include <sys/ppu_thread.h>
18
19#include "Orbis\OrbisExtras\ShutdownManager.h"
20
21
22LeaderboardManager *LeaderboardManager::m_instance = new OrbisLeaderboardManager(); //Singleton instance of the LeaderboardManager
23
24OrbisLeaderboardManager::OrbisLeaderboardManager()
25{
26 m_eStatsState = eStatsState_Idle;
27
28 m_titleContext = -1;
29
30 m_myXUID = INVALID_XUID;
31
32 m_scores = NULL; //m_stats = NULL;
33
34 m_statsType = eStatsType_Kills;
35 m_difficulty = 0;
36
37 m_requestId = 0;
38
39 m_openSessions = 0;
40
41 InitializeCriticalSection(&m_csViewsLock);
42
43 m_running = false;
44 m_threadScoreboard = NULL;
45}
46
47OrbisLeaderboardManager::~OrbisLeaderboardManager()
48{
49 m_running = false;
50
51 // 4J-JEV: Wait for thread to stop and hope it doesn't take too long.
52 long long startShutdown = System::currentTimeMillis();
53 while (m_threadScoreboard->isRunning())
54 {
55 Sleep(1);
56 assert( (System::currentTimeMillis() - startShutdown) < 16 );
57 }
58
59 delete m_threadScoreboard;
60
61 DeleteCriticalSection(&m_csViewsLock);
62}
63
64int OrbisLeaderboardManager::scoreboardThreadEntry(LPVOID lpParam)
65{
66 ShutdownManager::HasStarted(ShutdownManager::eLeaderboardThread);
67 OrbisLeaderboardManager *self = reinterpret_cast<OrbisLeaderboardManager *>(lpParam);
68
69 self->m_running = true;
70 app.DebugPrintf("[LeaderboardManager] Thread started.\n");
71
72 bool needsWriting = false;
73 do
74 {
75 if (self->m_openSessions > 0 || needsWriting)
76 {
77 self->scoreboardThreadInternal();
78 }
79
80 EnterCriticalSection(&self->m_csViewsLock);
81 needsWriting = self->m_views.size() > 0;
82 LeaveCriticalSection(&self->m_csViewsLock);
83
84 // 4J Stu - We can't write while we aren't signed in to live
85 if (!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad()))
86 {
87 needsWriting = false;
88 }
89
90 if ( (!needsWriting) && (self->m_eStatsState != eStatsState_Getting) )
91 {
92 Sleep(50); // 4J-JEV: When we're not reading or writing.
93 }
94
95 } while ( (self->m_running || self->m_eStatsState == eStatsState_Getting || needsWriting)
96 && ShutdownManager::ShouldRun(ShutdownManager::eLeaderboardThread)
97 );
98
99 // 4J-JEV, moved this here so setScore can finish up.
100 sceNpScoreDestroyTitleCtx(self->m_titleContext);
101 // TODO sceNpScoreTerm();
102 app.DebugPrintf("[LeaderboardManager] Thread closed.\n");
103 ShutdownManager::HasFinished(ShutdownManager::eLeaderboardThread);
104 return 0;
105}
106
107void OrbisLeaderboardManager::scoreboardThreadInternal()
108{
109 // 4J-JEV: Just initialise the context the once now.
110 if (m_titleContext == -1)
111 {
112 int primaryPad = ProfileManager.GetPrimaryPad();
113
114 if (!ProfileManager.IsSignedInLive(primaryPad)) return;
115
116 SceNpId npId;
117 ProfileManager.GetSceNpId(primaryPad,&npId);
118
119 int ret = sceNpScoreCreateNpTitleCtx(primaryPad, &npId);
120
121 if (ret < 0) return;
122 else m_titleContext = ret;
123 }
124 else assert( m_titleContext > 0 ); //Paranoia
125
126
127 switch (m_eStatsState)
128 {
129 case eStatsState_Getting:
130 // Player starts using async multiplayer feature
131 // 4J-PB - Fix for SCEA FQA #4 - TRC R4064 - Incorrect usage of AsyncMultiplay
132 // Note 1:
133 // The following NP call should be reserved for asynchronous multiplayer modes that require PS Plus to be accessed.
134 //
135 // Note 2:
136 // The message is not displayed with a user without PlayStation�Plus subscription and they are able to access the Leaderboards.
137
138 // NotifyAsyncPlusFeature();
139
140 switch(m_eFilterMode)
141 {
142 case eFM_MyScore:
143 case eFM_Friends:
144 getScoreByIds();
145 break;
146 case eFM_TopRank:
147 getScoreByRange();
148 break;
149 }
150 break;
151
152 case eStatsState_Canceled:
153 case eStatsState_Failed:
154 case eStatsState_Ready:
155 case eStatsState_Idle:
156
157 // 4J-JEV: Moved this here, I don't want reading and
158 // writing going on at the same time.
159 // --
160 // 4J-JEV: Writing no longer changes the manager state,
161 // we'll manage the write queue seperately.
162
163 EnterCriticalSection(&m_csViewsLock);
164 bool hasWork = !m_views.empty();
165 LeaveCriticalSection(&m_csViewsLock);
166
167 if (hasWork)
168 {
169 setScore();
170 }
171
172 break;
173 }
174}
175
176bool OrbisLeaderboardManager::getScoreByIds()
177{
178 if (m_eStatsState == eStatsState_Canceled) return false;
179
180 // ----------------------------
181 SceRtcTick last_sort_date;
182 SceNpScoreRankNumber mTotalRecord;
183
184 SceNpId *npIds = NULL;
185
186
187 int ret;
188 uint32_t num = 0;
189
190 SceNpScorePlayerRankData *ptr;
191 SceNpScoreComment *comments;
192 // ----------------------------
193
194 // Check for invalid LManager state.
195 assert( m_eFilterMode == eFM_Friends
196 || m_eFilterMode == eFM_MyScore);
197
198 SceNpId myNpId;
199 // 4J-PB - should it be user 0?
200 if(!ProfileManager.IsSignedInLive(0))
201 {
202 app.DebugPrintf("[LeaderboardManager] OpenSession() fail: User isn't signed in to PSN\n");
203 return false;
204 }
205 ProfileManager.GetSceNpId(0,&myNpId);
206
207 // Get queried users.
208 if (m_eFilterMode == eFM_Friends)
209 {
210
211 sce::Toolkit::NP::Utilities::Future<sce::Toolkit::NP::FriendsList> s_friendList;
212 sce::Toolkit::NP::FriendInfoRequest request;
213
214 memset(&request, 0, sizeof(request));
215 request.userInfo.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad());
216 request.flag = SCE_TOOLKIT_NP_FRIENDS_LIST_ALL;
217 ret = sce::Toolkit::NP::Friends::Interface::getFriendslist(&s_friendList, &request, false);
218
219 if(ret != SCE_TOOLKIT_NP_SUCCESS)
220 {
221 // Error handling
222 if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed;
223 app.DebugPrintf("[LeaderboardManager] getFriendslist fail\n");
224 return false;
225 }
226 else if (s_friendList.hasResult())
227 {
228 // 4J-JEV: Friends list doesn't include player, leave space for them.
229 num = s_friendList.get()->size() + 1;
230
231 npIds = new SceNpId[num];
232
233 int i = 0;
234
235 sce::Toolkit::NP::FriendsList::const_iterator itr;
236 for (itr = s_friendList.get()->begin(); itr != s_friendList.get()->end(); itr++)
237 {
238 npIds[i] = itr->npid;
239 i++;
240 }
241
242 npIds[num-1] = myNpId; // 4J-JEV: Append player to end of query.
243 }
244 else
245 {
246 // 4J-JEV: Something terrible must have happend,
247 // 'getFriendslist' was supposed to be a synchronous operation.
248 __debugbreak();
249
250 // 4J-JEV: We can at least fall-back to just the players score.
251 num = 1;
252 npIds = new SceNpId[1];
253 npIds[0] = myNpId;
254 }
255 }
256 else if (m_eFilterMode == eFM_MyScore)
257 {
258 num = 1;
259 npIds = new SceNpId[1];
260 npIds[0] = myNpId;
261 }
262
263 ptr = new SceNpScorePlayerRankData[num];
264 comments = new SceNpScoreComment[num];
265
266 ZeroMemory(ptr, sizeof(SceNpScorePlayerRankData) * num);
267 ZeroMemory(comments, sizeof(SceNpScoreComment) * num);
268
269 /* app.DebugPrintf("sceNpScoreGetRankingByNpId(\n\t transaction=%i,\n\t boardID=0,\n\t npId=%i,\n\t friendCount*sizeof(SceNpId)=%i*%i=%i,\
270 rankData=%i,\n\t friendCount*sizeof(SceNpScorePlayerRankData)=%i,\n\t NULL, 0, NULL, 0,\n\t friendCount=%i,\n...\n",
271 transaction, npId, friendCount, sizeof(SceNpId), friendCount*sizeof(SceNpId),
272 rankData, friendCount*sizeof(SceNpScorePlayerRankData), friendCount
273 ); */
274
275 int boardId = getBoardId(m_difficulty, m_statsType);
276
277 for (int batch=0; batch<num; batch+=100)
278 {
279 ret = sceNpScoreCreateTransactionCtx(m_titleContext);
280 if (m_eStatsState == eStatsState_Canceled)
281 {
282 // Cancel operation has been called, abort.
283 app.DebugPrintf("[LeaderboardManager]\tgetScoreByIds() - m_eStatsState == eStatsState_Canceled.\n");
284
285 sceNpScoreDestroyTransactionCtx(ret);
286
287 if (npIds != NULL) delete [] npIds;
288 if (ptr != NULL) delete [] ptr;
289 if (comments != NULL) delete [] comments;
290
291 return false;
292 }
293 else if (ret < 0)
294 {
295 // Error occurred creating a transacion, abort.
296 app.DebugPrintf("[LeaderboardManager]\tgetScoreByIds() - createTransaction failed, ret=0x%X\n", ret);
297
298 m_eStatsState = eStatsState_Failed;
299
300 if (npIds != NULL) delete [] npIds;
301 if (ptr != NULL) delete [] ptr;
302 if (comments != NULL) delete [] comments;
303
304 return false;
305 }
306 else
307 {
308 // Transaction created successfully, continue.
309 m_requestId = ret;
310 }
311
312 int tmpNum = min( num - batch, (unsigned int) 100 );
313
314 app.DebugPrintf("[LeaderboardManager]\t Requesting ids %i-%i of %i.\n", batch, batch+tmpNum, num);
315
316 ret = sceNpScoreGetRankingByNpId(
317 m_requestId,
318 boardId, // BoardId
319
320 batch + npIds, sizeof(SceNpId) * tmpNum, //IN: Player IDs
321 batch + ptr, sizeof(SceNpScorePlayerRankData) * tmpNum, //OUT: Rank Data
322
323 batch + comments, sizeof(SceNpScoreComment) * tmpNum, //OUT: Comments
324
325 NULL, 0, // GameData. (unused)
326
327 tmpNum,
328
329 &last_sort_date,
330 &mTotalRecord,
331
332 NULL // Reserved, specify null.
333 );
334
335 if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED)
336 {
337 ret = sceNpScoreDestroyTransactionCtx(m_requestId);
338 app.DebugPrintf("[LeaderboardManager] getScoreByIds(): 'sceNpScoreGetRankingByRange' aborted (0x%X).\n", ret);
339
340 delete [] ptr;
341 delete [] comments;
342 delete [] npIds;
343
344 return false;
345 }
346 else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND)
347 {
348 // 4J-JEV: Keep going, other batches might have scores.
349 }
350 else if (ret<0) goto error3;
351
352 // Return.
353 sceNpScoreDestroyTransactionCtx(m_requestId);
354 m_requestId = 0;
355 }
356
357 m_readCount = num;
358
359 // Filter scorers and construct output structure.
360 if (m_scores != NULL) delete [] m_scores;
361 m_scores = new ReadScore[m_readCount];
362 convertToOutput(m_readCount, m_scores, ptr, comments);
363 m_maxRank = m_readCount;
364
365 app.DebugPrintf(
366 "[LeaderboardManager] getScoreByIds(), Success!\n"
367 "\t Board %i\n"
368 "\t %i of %i results have an entry\n"
369 "1stScore=%i\n",
370 boardId,
371 m_readCount, num,
372 ptr->rankData.scoreValue
373 );
374
375 // Sort scores
376 std::sort(m_scores, m_scores + m_readCount, SortByRank);
377
378 delete [] ptr;
379 delete [] comments;
380 delete [] npIds;
381
382 m_eStatsState = eStatsState_Ready;
383 return true;
384
385 // Error.
386error3:
387 if (ret!=SCE_NP_COMMUNITY_ERROR_ABORTED) //0x8002a109
388 sceNpScoreDestroyTransactionCtx(m_requestId);
389 m_requestId = 0;
390 delete [] ptr;
391 delete [] comments;
392error2:
393 if (npIds != NULL) delete [] npIds;
394error1:
395 if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed;
396 app.DebugPrintf("[LeaderboardManger] getScoreByIds() FAILED, ret=0x%X\n", ret);
397 return false;
398}
399
400bool OrbisLeaderboardManager::getScoreByRange()
401{
402 SceRtcTick last_sort_date;
403 SceNpScoreRankNumber mTotalRecord;
404
405 unsigned int num = m_readCount;
406 SceNpScoreRankData *ptr;
407 SceNpScoreComment *comments;
408
409 assert(m_eFilterMode == eFM_TopRank);
410
411 int ret = sceNpScoreCreateTransactionCtx(m_titleContext);
412 if (m_eStatsState == eStatsState_Canceled)
413 {
414 // Cancel operation has been called, abort.
415 app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() - m_eStatsState == eStatsState_Canceled.\n");
416 sceNpScoreDestroyTransactionCtx(ret);
417 return false;
418 }
419 else if (ret < 0)
420 {
421 // Error occurred creating a transacion, abort.
422 m_eStatsState = eStatsState_Failed;
423 app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() - createTransaction failed, ret=0x%X\n", ret);
424 return false;
425 }
426 else
427 {
428 // Transaction created successfully, continue.
429 m_requestId = ret;
430 }
431
432 ptr = new SceNpScoreRankData[num];
433 comments = new SceNpScoreComment[num];
434
435 int boardId = getBoardId(m_difficulty, m_statsType);
436 ret = sceNpScoreGetRankingByRange(
437 m_requestId,
438 boardId, // BoardId
439
440 m_startIndex,
441
442 ptr, sizeof(SceNpScoreRankData) * num, //OUT: Rank Data
443
444 comments, sizeof(SceNpScoreComment) * num, //OUT: Comment Data
445
446 NULL, 0, // GameData.
447
448 num,
449
450 &last_sort_date,
451 &m_maxRank, // 'Total number of players registered in the target scoreboard.'
452
453 NULL // Reserved, specify null.
454 );
455
456 if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED)
457 {
458 ret = sceNpScoreDestroyTransactionCtx(m_requestId);
459 app.DebugPrintf("[LeaderboardManager] getScoreByRange(): 'sceNpScoreGetRankingByRange' aborted (0x%X).\n", ret);
460
461 delete [] ptr;
462 delete [] comments;
463
464 return false;
465 }
466 else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND)
467 {
468 ret = sceNpScoreDestroyTransactionCtx(m_requestId);
469 app.DebugPrintf("[LeaderboardManager] getScoreByRange(): Game ranking not found.");
470
471 delete [] ptr;
472 delete [] comments;
473
474 m_scores = NULL;
475 m_readCount = 0;
476
477 m_eStatsState = eStatsState_Ready;
478 return false;
479 }
480 else if (ret<0) goto error2;
481 else
482 {
483 app.DebugPrintf("[LeaderboardManager] getScoreByRange(), success, 1stScore=%i.\n", ptr->scoreValue);
484 }
485
486 // Return.
487 sceNpScoreDestroyTransactionCtx(m_requestId);
488 m_requestId = 0;
489
490 //m_stats = ptr; //Maybe: addPadding(num,ptr);
491
492 if (m_scores != NULL) delete [] m_scores;
493 m_readCount = ret;
494 m_scores = new ReadScore[m_readCount];
495 for (int i=0; i<m_readCount; i++)
496 {
497 //memcpy(m_scores+i, ptr+i, sizeof(SceNpScoreRankData));
498 initReadScoreStruct(m_scores[i], ptr[i]);
499 //fromBase32(m_scores+i, comments+i);
500 fillReadScoreStruct(m_scores[i], comments[i]);
501 }
502
503 m_eStatsState = eStatsState_Ready;
504 return true;
505
506 // Error.
507error2:
508 if (ret!=SCE_NP_COMMUNITY_ERROR_ABORTED) //0x8002a109
509 sceNpScoreDestroyTransactionCtx(m_requestId);
510 m_requestId = 0;
511
512 delete [] ptr;
513 delete [] comments;
514error1:
515 if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed;
516 app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() failed, ret=0x%X\n", ret);
517 return false;
518}
519
520bool OrbisLeaderboardManager::setScore()
521{
522 int ret;
523 SceNpId npId;
524 int32_t writeTitleContext = 0;
525 SceNpScoreRankNumber tmp = 0;
526 SceNpScoreComment comment;
527
528 // Get next job.
529
530 EnterCriticalSection(&m_csViewsLock);
531 RegisterScore rscore = m_views.front();
532 m_views.pop();
533 LeaveCriticalSection(&m_csViewsLock);
534
535 if ( ProfileManager.IsGuest(rscore.m_iPad) )
536 {
537 app.DebugPrintf("[LeaderboardManager] setScore(): m_iPad[%i] is guest.\n", rscore.m_iPad);
538 return true;
539 }
540
541 ProfileManager.GetSceNpId(rscore.m_iPad, &npId);
542 writeTitleContext = sceNpScoreCreateNpTitleCtx( 0, &npId);
543 if (writeTitleContext < 0)
544 {
545 app.DebugPrintf("[LeaderboardManager] setScore(): sceNpScoreCreateTitleCtx FAILED, ret == %X.\n", ret);
546 return false;
547 }
548
549 ret = sceNpScoreCreateTransactionCtx(writeTitleContext);
550
551 // Start emptying queue if leaderboards has been closed.
552 if (ret == SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED)
553 {
554 EnterCriticalSection(&m_csViewsLock);
555 m_views.pop();
556 LeaveCriticalSection(&m_csViewsLock);
557 }
558
559 // Error handling.
560 if (ret<0)
561 {
562 app.DebugPrintf("[LeaderboardManager] setScore() FAILED, ret=0x%X\n", ret);
563 sceNpScoreDestroyTitleCtx(writeTitleContext);
564 return false;
565 }
566 m_requestId = ret;
567
568 toBase32(&comment, (void *) &rscore.m_commentData);
569
570 int boardId = getBoardId(rscore.m_difficulty, rscore.m_commentData.m_statsType);
571 ret = sceNpScoreRecordScore(
572 m_requestId, // transId,
573 boardId, // boardId,
574 rscore.m_score, //IN: new score,
575
576 &comment, // Comments
577 NULL, // GameInfo
578
579 &tmp, //OUT: current rank,
580 NULL, //compareDate
581
582 NULL // Reserved, specify null.
583 );
584
585 if (ret==SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE) //0x8002A415
586 app.DebugPrintf("[LeaderboardManager] setScore(), doesn't beat current score, %i.\n", tmp);
587 else if (ret==SCE_NP_COMMUNITY_ERROR_ABORTED) goto error1;//0x8002a109
588 else if (ret<0) goto error2;
589 else
590 {
591 app.DebugPrintf("[LeaderboardManager] setScore(), success. boardId=%i, score=%i\n", boardId, rscore.m_score);
592 }
593
594 // Return.
595 sceNpScoreDestroyTransactionCtx(m_requestId);
596 m_requestId = 0;
597 //m_eStatsState = eStatsState_Idle;
598 return true;
599
600 // Error.
601error2:
602 sceNpScoreDestroyTransactionCtx(m_requestId);
603 m_requestId = 0;
604error1:
605 app.DebugPrintf("[LeaderboardManager] setScore() FAILED, ret=0x%X\n", ret);
606 sceNpScoreDestroyTitleCtx(writeTitleContext);
607 return false;
608}
609
610void OrbisLeaderboardManager::Tick()
611{
612 ReadView view;
613
614 switch( m_eStatsState )
615 {
616 case eStatsState_Ready:
617 {
618 assert(m_scores != NULL || m_readCount == 0);
619
620 view.m_numQueries = m_readCount;
621 view.m_queries = m_scores;
622
623 // 4J-JEV: Debugging.
624 //LeaderboardManager::printStats(view);
625
626 eStatsReturn ret = eStatsReturn_NoResults;
627 if (view.m_numQueries > 0)
628 ret = eStatsReturn_Success;
629
630 if (m_readListener != NULL)
631 {
632 app.DebugPrintf("[LeaderboardManager] OnStatsReadComplete(%i, %i, _), m_readCount=%i.\n", ret, m_maxRank, m_readCount);
633 m_readListener->OnStatsReadComplete(ret, m_maxRank, view);
634 }
635
636 m_eStatsState = eStatsState_Idle;
637
638 delete [] m_scores;
639 m_scores = NULL;
640 }
641 break;
642
643 case eStatsState_Failed:
644 {
645 view.m_numQueries = 0;
646 view.m_queries = NULL;
647
648 if ( m_readListener != NULL )
649 m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, 0, view);
650
651 m_eStatsState = eStatsState_Idle;
652 }
653 break;
654
655 case eStatsState_Canceled:
656 {
657 m_eStatsState = eStatsState_Idle;
658 }
659 break;
660
661 default: // Getting or Idle.
662 break;
663 }
664}
665
666bool OrbisLeaderboardManager::OpenSession()
667{
668 if (m_openSessions == 0)
669 {
670 if (m_threadScoreboard == NULL)
671 {
672 m_threadScoreboard = new C4JThread(&scoreboardThreadEntry, this, "4JScoreboard");
673 m_threadScoreboard->SetProcessor(CPU_CORE_LEADERBOARDS);
674 m_threadScoreboard->SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
675 m_threadScoreboard->Run();
676 }
677
678 app.DebugPrintf("[LeaderboardManager] OpenSession(): Starting sceNpScore utility.\n");
679 }
680 else
681 {
682 app.DebugPrintf("[LeaderboardManager] OpenSession(): Another session opened, total=%i\n", m_openSessions+1);
683 }
684
685 m_openSessions++;
686 return true;
687}
688
689void OrbisLeaderboardManager::CloseSession()
690{
691 m_openSessions--;
692
693 if (m_openSessions == 0) app.DebugPrintf("[LeaderboardManager] CloseSession(): Quitting sceNpScore utility.\n");
694 else app.DebugPrintf("[LeaderboardManager] CloseSession(): %i sessions still open.\n", m_openSessions);
695}
696
697void OrbisLeaderboardManager::DeleteSession() {}
698
699bool OrbisLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views)
700{
701 // Need to cancel read/write operation first.
702 //if (m_eStatsState != eStatsState_Idle) return false;
703
704 // Write relevant parameters.
705 //RegisterScore *regScore = reinterpret_cast<RegisterScore *>(views);
706
707 EnterCriticalSection(&m_csViewsLock);
708 for (int i=0; i<viewCount; i++)
709 {
710 app.DebugPrintf("[LeaderboardManager] WriteStats(), starting. difficulty=%i, statsType=%i, score=%i\n",
711 views[i].m_difficulty, views[i].m_commentData.m_statsType, views[i].m_score);
712
713 m_views.push(views[i]);
714 }
715 LeaveCriticalSection(&m_csViewsLock);
716
717 delete [] views; //*regScore;
718
719 //m_eStatsState = eStatsState_Writing;
720 return true;
721}
722
723// myUID ignored on PS3.
724bool OrbisLeaderboardManager::ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount)
725{
726 // Need to cancel read/write operation first.
727 if (m_eStatsState != eStatsState_Idle) return false;
728 if (!LeaderboardManager::ReadStats_Friends(listener, difficulty, type, myUID, startIndex, readCount)) return false;
729
730 //int ret = sceNpManagerGetNpId(&m_myNpId);
731 //if (ret<0) return false;
732
733 m_eStatsState = eStatsState_Getting;
734 return true;
735}
736
737// myUID ignored on PS3.
738bool OrbisLeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount)
739{
740 // Need to cancel read/write operation first.
741 if (m_eStatsState != eStatsState_Idle) return false;
742 if (!LeaderboardManager::ReadStats_MyScore(listener, difficulty, type, myUID, readCount)) return false;
743
744 //int ret = sceNpManagerGetNpId(&m_myNpId);
745 //if (ret<0) return false;
746
747 m_eStatsState = eStatsState_Getting;
748 return true;
749}
750
751// myUID ignored on PS3.
752bool OrbisLeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount)
753{
754 // Need to cancel read/write operation first.
755 if (m_eStatsState != eStatsState_Idle) return false;
756 if (!LeaderboardManager::ReadStats_TopRank(listener, difficulty, type, startIndex, readCount)) return false;
757 m_eStatsState = eStatsState_Getting;
758 return true;
759}
760
761void OrbisLeaderboardManager::FlushStats() {}
762
763void OrbisLeaderboardManager::CancelOperation()
764{
765 m_readListener = NULL;
766 m_eStatsState = eStatsState_Canceled;
767
768 if (m_requestId != 0)
769 {
770 int ret = sceNpScoreAbortTransaction(m_requestId);
771
772 if (ret < 0)
773 {
774 app.DebugPrintf("[LeaderboardManager] CancelOperation(): Problem encountered aborting current operation, 0x%X.\n", ret);
775 }
776 else
777 {
778 app.DebugPrintf("[LeaderboardManager] CancelOperation(): Operation aborted successfully.\n");
779 }
780 }
781 else
782 {
783 app.DebugPrintf("[LeaderboardManager] CancelOperation(): No current operation.\n");
784 }
785}
786
787bool OrbisLeaderboardManager::isIdle()
788{
789 return m_eStatsState == eStatsState_Idle;
790}
791
792int OrbisLeaderboardManager::getBoardId(int difficulty, EStatsType statsType)
793{
794 switch (statsType)
795 {
796 case eStatsType_Travelling:
797 if (0 <= difficulty && difficulty < 4) return 1 + difficulty; // [1,2,3,4]
798 else return -1;
799
800 case eStatsType_Mining:
801 if (0 <= difficulty && difficulty < 4) return 5 + difficulty; // [5,6,7,8]
802 else return -1;
803
804 case eStatsType_Farming:
805 if (0 <= difficulty && difficulty < 4) return 9 + difficulty; // [9,10,11,12]
806 else return -1;
807
808 case eStatsType_Kills:
809 if (1 <= difficulty && difficulty < 4) return 13 + difficulty - 1; // [13,14,15,16]
810 else return -1;
811
812 default: return -1;
813 }
814}
815
816// 4J-JEV: Filter out all friends who don't have scores.
817/*
818SceNpScoreRankData *OrbisLeaderboardManager::filterJustScorers(unsigned int &num, SceNpScorePlayerRankData *friendsData)
819{
820int num2 = 0;
821for (int i=0; i<num; i++) if (friendsData[i].hasData) num2++;
822num = num2; num2 = 0;
823SceNpScoreRankData *out = new SceNpScoreRankData[num];
824for (int i=0; i<num; i++) if (friendsData[i].hasData) out[num2++] = friendsData[i].rankData;
825return out;
826} */
827
828// 4J-JEV: Unused, here if we want to switch LeaderboardManager::ViewOut to 'SceNpScorePlayerRankData'.
829SceNpScorePlayerRankData *OrbisLeaderboardManager::addPadding(unsigned int num, SceNpScoreRankData *rankData)
830{
831 SceNpScorePlayerRankData *out = new SceNpScorePlayerRankData[num];
832 for (int i=0; i<num; i++)
833 {
834 out[i].hasData = true;
835 out[i].rankData = rankData[i];
836 }
837 delete [] rankData;
838 return out;
839}
840
841// 4J-JEV: Filter and create output object.
842void OrbisLeaderboardManager::convertToOutput(unsigned int &num, ReadScore *out, SceNpScorePlayerRankData *rankData, SceNpScoreComment *comm)
843{
844 int j=0;
845 for (int i=0; i<num; i++)
846 {
847 if (rankData[i].hasData)
848 {
849 initReadScoreStruct( out[j], rankData[i].rankData );
850 fillReadScoreStruct( out[j], comm[i] );
851
852 j++;
853 }
854 }
855
856 num = j;
857}
858
859void OrbisLeaderboardManager::toBinary(void *out, SceNpScoreComment *in)
860{
861 string decoded = base64_decode( string((char*)in) );
862 memcpy(out, decoded.c_str(), RECORD_SIZE);
863}
864
865void OrbisLeaderboardManager::fromBinary(SceNpScoreComment **out, void *in)
866{
867 *out = (SceNpScoreComment *) new unsigned char[SCE_NP_SCORE_COMMENT_MAXLEN + 1];
868 string encoded = base64_encode((unsigned char const *) in, RECORD_SIZE);
869 memcpy(out, encoded.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN);
870}
871
872void OrbisLeaderboardManager::toBase32(SceNpScoreComment *out, void *in)
873{
874 ZeroMemory(out,sizeof(SceNpScoreComment));
875 PBYTE bytes = (PBYTE) in;
876 char *chars = out->utf8Comment;
877
878 for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++)
879 {
880 int sByte = (i*5) / 8;
881 int eByte = (5+(i*5)) / 8;
882 int dIndex = (i*5) % 8;
883
884 unsigned char fivebits = 0;
885
886 fivebits = *(bytes+sByte) << dIndex;
887
888 if (eByte != sByte)
889 fivebits = fivebits | *(bytes+eByte) >> (8-dIndex);
890
891 fivebits = (fivebits>>3) & 0x1F;
892
893 if (fivebits < 10) // 0 - 9
894 chars[i] = '0' + fivebits;
895 else if (fivebits < 32) // A - V
896 chars[i] = 'A' + (fivebits-10);
897 else
898 assert(false);
899 }
900
901 toSymbols(out->utf8Comment);
902}
903
904void OrbisLeaderboardManager::fromBase32(void *out, SceNpScoreComment *in)
905{
906 PBYTE bytes = (PBYTE) out;
907 ZeroMemory(bytes, RECORD_SIZE);
908
909 fromSymbols(in->utf8Comment);
910
911 char ch[2] = { 0, 0 };
912 for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++)
913 {
914 ch[0] = in->utf8Comment[i];
915 unsigned char fivebits = strtol(ch, NULL, 32) << 3;
916
917 int sByte = (i*5) / 8;
918 int eByte = (5+(i*5)) / 8;
919 int dIndex = (i*5) % 8;
920
921 *(bytes + sByte) = *(bytes+sByte) | (fivebits >> dIndex);
922
923 if (eByte != sByte)
924 *(bytes + eByte) = fivebits << (8-dIndex);
925 }
926}
927
928char symbBase32[32] = {
929 ' ', '!','\"', '#', '$', '%', '&','\'', '(', ')',
930 '*', '+', '`', '-', '.', '/', ':', ';', '<', '=',
931 '>', '?', '[','\\', ']', '^', '_', '{', '|', '}',
932 '~', '@'
933};
934
935char charBase32[32] = {
936 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
937 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
938 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
939 'U', 'V'
940};
941
942void OrbisLeaderboardManager::toSymbols(char *str)
943{
944 for (int i = 0; i < 63; i++)
945 {
946 for (int j=0; j < 32; j++)
947 {
948 if (str[i]==charBase32[j])
949 str[i] =symbBase32[j];
950 }
951 }
952}
953
954void OrbisLeaderboardManager::fromSymbols(char *str)
955{
956 for (int i = 0; i < 63; i++)
957 {
958 for (int j=0; j < 32; j++)
959 {
960 if (str[i]==symbBase32[j])
961 str[i] =charBase32[j];
962 }
963 }
964}
965
966bool OrbisLeaderboardManager::test_string(string testing)
967{
968#ifndef _CONTENT_PACKAGE
969 static SceNpScoreComment comment;
970 ZeroMemory(&comment, sizeof(SceNpScoreComment));
971 memcpy(&comment, testing.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN);
972
973 int ctx = sceNpScoreCreateTransactionCtx(m_titleContext);
974 if (ctx<0) return false;
975
976 int ret = sceNpScoreCensorComment(ctx, (const char *) &comment, NULL);
977
978 if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_CENSORED)
979 {
980 app.DebugPrintf("\n[TEST_STRING]: REJECTED ");
981 }
982 else if (ret < 0)
983 {
984 sceNpScoreDestroyTransactionCtx(ctx);
985 return false;
986 }
987 else
988 {
989 app.DebugPrintf("\n[TEST_STRING]: permitted ");
990 }
991
992 app.DebugPrintf("'%s'\n", comment.utf8Comment);
993 sceNpScoreDestroyTransactionCtx(ctx);
994 return true;
995#else
996 return true;
997#endif
998}
999
1000void OrbisLeaderboardManager::initReadScoreStruct(ReadScore &out, SceNpScoreRankData &rankData)
1001{
1002 ZeroMemory(&out, sizeof(ReadScore));
1003
1004 // Init rank and onlineID
1005 out.m_uid.setOnlineID( rankData.npId.handle, true );
1006 out.m_rank = rankData.rank;
1007
1008 // Convert to wstring and copy name.
1009 wstring wstrName = convStringToWstring( string(rankData.npId.handle.data) ).c_str();
1010 //memcpy(&out.m_name, wstrName.c_str(), XUSER_NAME_SIZE);
1011 out.m_name=wstrName;
1012}
1013
1014void OrbisLeaderboardManager::fillReadScoreStruct(ReadScore &out, SceNpScoreComment &comment)
1015{
1016 StatsData statsData;
1017 fromBase32( (void *) &statsData, &comment );
1018
1019 switch (statsData.m_statsType)
1020 {
1021 case eStatsType_Farming:
1022 out.m_statsSize = 6;
1023 out.m_statsData[0] = statsData.m_farming.m_eggs;
1024 out.m_statsData[1] = statsData.m_farming.m_wheat;
1025 out.m_statsData[2] = statsData.m_farming.m_mushroom;
1026 out.m_statsData[3] = statsData.m_farming.m_sugarcane;
1027 out.m_statsData[4] = statsData.m_farming.m_milk;
1028 out.m_statsData[5] = statsData.m_farming.m_pumpkin;
1029 break;
1030 case eStatsType_Mining:
1031 out.m_statsSize = 7;
1032 out.m_statsData[0] = statsData.m_mining.m_dirt;
1033 out.m_statsData[1] = statsData.m_mining.m_cobblestone;
1034 out.m_statsData[2] = statsData.m_mining.m_sand;
1035 out.m_statsData[3] = statsData.m_mining.m_stone;
1036 out.m_statsData[4] = statsData.m_mining.m_gravel;
1037 out.m_statsData[5] = statsData.m_mining.m_clay;
1038 out.m_statsData[6] = statsData.m_mining.m_obsidian;
1039 break;
1040 case eStatsType_Kills:
1041 out.m_statsSize = 7;
1042 out.m_statsData[0] = statsData.m_kills.m_zombie;
1043 out.m_statsData[1] = statsData.m_kills.m_skeleton;
1044 out.m_statsData[2] = statsData.m_kills.m_creeper;
1045 out.m_statsData[3] = statsData.m_kills.m_spider;
1046 out.m_statsData[4] = statsData.m_kills.m_spiderJockey;
1047 out.m_statsData[5] = statsData.m_kills.m_zombiePigman;
1048 out.m_statsData[6] = statsData.m_kills.m_slime;
1049 break;
1050 case eStatsType_Travelling:
1051 out.m_statsSize = 4;
1052 out.m_statsData[0] = statsData.m_travelling.m_walked;
1053 out.m_statsData[1] = statsData.m_travelling.m_fallen;
1054 out.m_statsData[2] = statsData.m_travelling.m_minecart;
1055 out.m_statsData[3] = statsData.m_travelling.m_boat;
1056 break;
1057 }
1058}
1059
1060bool OrbisLeaderboardManager::SortByRank(const ReadScore &lhs, const ReadScore &rhs)
1061{
1062 return lhs.m_rank < rhs.m_rank;
1063}
1064
1065// Notify plus feature use
1066/*void OrbisLeaderboardManager::NotifyAsyncPlusFeature()
1067{
1068 SceNpNotifyPlusFeatureParameter param = SceNpNotifyPlusFeatureParameter();
1069 param.userId = ProfileManager.getUserID(ProfileManager.GetPrimaryPad());
1070 param.size = sizeof(SceNpNotifyPlusFeatureParameter);
1071 param.features = SCE_NP_PLUS_FEATURE_ASYNC_MULTIPLAY;
1072 ZeroMemory(param.padding, sizeof(char) * 4);
1073 ZeroMemory(param.reserved, sizeof(uint8_t) * 32);
1074
1075 int err = sceNpNotifyPlusFeature(¶m);
1076 if (err != SCE_OK)
1077 {
1078 app.DebugPrintf("OrbisLeaderboardManager::NotifyAsyncPlusFeature: sceNpNotifyPlusFeature failed (0x%x)\n", err);
1079 assert(0);
1080 }
1081}*/