diff options
| author | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
|---|---|---|
| committer | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
| commit | b691c43c44ff180d10e7d4a9afc83b98551ff586 (patch) | |
| tree | 3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/PS3/Leaderboards | |
| parent | def8cb415354ac390b7e89052a50605285f1aca9 (diff) | |
Initial commit
Diffstat (limited to 'Minecraft.Client/PS3/Leaderboards')
| -rw-r--r-- | Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.cpp | 1048 | ||||
| -rw-r--r-- | Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.h | 110 | ||||
| -rw-r--r-- | Minecraft.Client/PS3/Leaderboards/base64.cpp | 131 | ||||
| -rw-r--r-- | Minecraft.Client/PS3/Leaderboards/base64.h | 7 |
4 files changed, 1296 insertions, 0 deletions
diff --git a/Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.cpp b/Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.cpp new file mode 100644 index 00000000..38380bfb --- /dev/null +++ b/Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.cpp @@ -0,0 +1,1048 @@ +#include "stdafx.h" + +#include "PS3LeaderboardManager.h" + +#include "base64.h" + +#include "..\PS3_App.h" +#include "..\..\Common\Consoles_App.h" + +#include "Common\Network\Sony\SQRNetworkManager.h" + +#include "..\..\..\Minecraft.World\StringHelpers.h" + +#include <cstdlib> + +#include <np.h> +#include <sys/ppu_thread.h> + +#include "PS3\PS3Extras\ShutdownManager.h" + +#ifdef __PS3__ +LeaderboardManager *LeaderboardManager::m_instance = new PS3LeaderboardManager(); //Singleton instance of the LeaderboardManager +#endif + +PS3LeaderboardManager::PS3LeaderboardManager() +{ + m_eStatsState = eStatsState_Idle; + + m_titleContext = -1; + + m_myXUID = INVALID_XUID; + + m_scores = NULL; //m_stats = NULL; + + m_statsType = eStatsType_Kills; + m_difficulty = 0; + + m_transactionCtx = 0; + + m_openSessions = 0; + + InitializeCriticalSection(&m_csViewsLock); + + m_running = false; + m_threadScoreboard = NULL; +} + +PS3LeaderboardManager::~PS3LeaderboardManager() +{ + m_running = false; + + // 4J-JEV: Wait for thread to stop and hope it doesn't take too long. + long long startShutdown = System::currentTimeMillis(); + while (m_threadScoreboard->isRunning()) + { + Sleep(1); + assert( (System::currentTimeMillis() - startShutdown) < 16 ); + } + + delete m_threadScoreboard; + + DeleteCriticalSection(&m_csViewsLock); +} + +int PS3LeaderboardManager::scoreboardThreadEntry(LPVOID lpParam) +{ + ShutdownManager::HasStarted(ShutdownManager::eLeaderboardThread); + PS3LeaderboardManager *self = reinterpret_cast<PS3LeaderboardManager *>(lpParam); + + self->m_running = true; + app.DebugPrintf("[LeaderboardManager] Thread started.\n"); + + bool needsWriting = false; + do + { + if (self->m_openSessions > 0 || needsWriting) + { + self->scoreboardThreadInternal(); + } + + EnterCriticalSection(&self->m_csViewsLock); + needsWriting = self->m_views.size() > 0; + LeaveCriticalSection(&self->m_csViewsLock); + + // 4J Stu - We can't write while we aren't signed in to live + if (!ProfileManager.IsSignedInLive(ProfileManager.GetPrimaryPad())) + { + needsWriting = false; + } + + if ( (!needsWriting) && (self->m_eStatsState != eStatsState_Getting) ) + { + Sleep(50); // 4J-JEV: When we're not reading or writing. + } + + } while ( (self->m_running || self->m_eStatsState == eStatsState_Getting || needsWriting) + && ShutdownManager::ShouldRun(ShutdownManager::eLeaderboardThread) + ); + + // 4J-JEV, moved this here so setScore can finish up. + sceNpScoreDestroyTitleCtx(self->m_titleContext); + sceNpScoreTerm(); + app.DebugPrintf("[LeaderboardManager] Thread closed.\n"); + ShutdownManager::HasFinished(ShutdownManager::eLeaderboardThread); + return 0; +} + +void PS3LeaderboardManager::scoreboardThreadInternal() +{ + // 4J-JEV: Just initialise the context the once now. + if (m_titleContext == -1) + { + //CD - Init NP Score + int ret = sceNpScoreInit(); + if ( ret < 0 ) + { + if ( ret != SCE_NP_COMMUNITY_ERROR_ALREADY_INITIALIZED ) + { + app.DebugPrintf("[LeaderboardManager] sceNpScoreInit() failed. ret = 0x%x\n", ret); + return; + } + else + { + app.DebugPrintf("[LeaderboardManager] sceNpScoreInit() already initialised, (0x%x)\n", ret); + } + } + + + SceNpId npId; + ret = sceNpManagerGetNpId(&npId); + if (ret < 0) + { + app.DebugPrintf("[LeaderboardManager] OpenSession(): sceNpManagerGetNpId FAILED, ret == %X.\n", ret); + return; + } + + ret = sceNpScoreCreateTitleCtx( &s_npCommunicationId, &s_npCommunicationPassphrase, &npId ); + + if (ret < 0) + { + app.DebugPrintf("[LeaderboardManager] OpenSession(): sceNpScoreCreateTitleCtx FAILED, ret == %X.\n", ret); + return; + } + else + { + m_titleContext = ret; + } + } + else assert( m_titleContext > 0 ); //Paranoia + + + switch (m_eStatsState) + { + case eStatsState_Getting: + switch(m_eFilterMode) + { + case eFM_MyScore: + case eFM_Friends: + getScoreByIds(); + break; + case eFM_TopRank: + getScoreByRange(); + break; + } + break; + + case eStatsState_Canceled: + case eStatsState_Failed: + case eStatsState_Ready: + case eStatsState_Idle: + + // 4J-JEV: Moved this here, I don't want reading and + // writing going on at the same time. + // -- + // 4J-JEV: Writing no longer changes the manager state, + // we'll manage the write queue seperately. + + EnterCriticalSection(&m_csViewsLock); + bool hasWork = !m_views.empty(); + LeaveCriticalSection(&m_csViewsLock); + + if (hasWork) + { + setScore(); + } + + break; + } +} + +bool PS3LeaderboardManager::getScoreByIds() +{ + if (m_eStatsState == eStatsState_Canceled) return false; + + // ---------------------------- + CellRtcTick last_sort_date; + SceNpScoreRankNumber mTotalRecord; + + SceNpId *npIds = NULL; + + + int ret; + uint32_t num = 0; + + SceNpScorePlayerRankData *ptr; + SceNpScoreComment *comments; + // ---------------------------- + + // Check for invalid LManager state. + assert( m_eFilterMode == eFM_Friends + || m_eFilterMode == eFM_MyScore); + + SceNpId myNpId; + ret = sceNpManagerGetNpId(&myNpId); + if (ret<0) goto error1; + + // Get queried users. + if (m_eFilterMode == eFM_Friends) + { + // 4J-JEV: Doesn't include the player (its just their friends). + ret = sceNpBasicGetFriendListEntryCount(&num); + num += 1; + + npIds = new SceNpId[num]; + + for (uint32_t i = 0; i < num-1; i++) + { + ret = sceNpBasicGetFriendListEntry(i, npIds+i); + if (ret<0) goto error2; + + } + npIds[num-1] = myNpId; // 4J-JEV: Append player to end of query. + } + else if (m_eFilterMode == eFM_MyScore) + { + num = 1; + npIds = new SceNpId[1]; + npIds[0] = myNpId; + } + + ret = sceNpScoreCreateTransactionCtx(m_titleContext); + if (m_eStatsState == eStatsState_Canceled) + { + // Cancel operation has been called, abort. + app.DebugPrintf("[LeaderboardManager]\tgetScoreByIds() - m_eStatsState == eStatsState_Canceled.\n"); + + sceNpScoreDestroyTransactionCtx(ret); + + if (npIds != NULL) delete [] npIds; + return false; + } + else if (ret < 0) + { + // Error occurred creating a transacion, abort. + app.DebugPrintf("[LeaderboardManager]\tgetScoreByIds() - createTransaction failed, ret=0x%X\n", ret); + + m_eStatsState = eStatsState_Failed; + + if (npIds != NULL) delete [] npIds; + return false; + } + else + { + // Transaction created successfully, continue. + m_transactionCtx = ret; + app.DebugPrintf("[LeaderboardManager] REQUEST ID A = 0x%X\n", m_transactionCtx); + } + + ptr = new SceNpScorePlayerRankData[num]; + comments = new SceNpScoreComment[num]; + + /* app.DebugPrintf("sceNpScoreGetRankingByNpId(\n\t transaction=%i,\n\t boardID=0,\n\t npId=%i,\n\t friendCount*sizeof(SceNpId)=%i*%i=%i,\ + rankData=%i,\n\t friendCount*sizeof(SceNpScorePlayerRankData)=%i,\n\t NULL, 0, NULL, 0,\n\t friendCount=%i,\n...\n", + transaction, npId, friendCount, sizeof(SceNpId), friendCount*sizeof(SceNpId), + rankData, friendCount*sizeof(SceNpScorePlayerRankData), friendCount + ); */ + + int boardId = getBoardId(m_difficulty, m_statsType); + ret = sceNpScoreGetRankingByNpId( + m_transactionCtx, + boardId, // BoardId + + npIds, sizeof(SceNpId) * num, //IN: Player IDs + ptr, sizeof(SceNpScorePlayerRankData) * num, //OUT: Rank Data + + comments, sizeof(SceNpScoreComment) * num, //OUT: Comments + + NULL, 0, // GameData. (unused) + + num, + + &last_sort_date, + &mTotalRecord, + + NULL // Reserved, specify null. + ); + + if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED) + { + app.DebugPrintf("[LeaderboardManager] getScoreByIds(): 'sceNpScoreGetRankingByRange' aborted (0x%X).\n", ret); + + ret = sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + + delete [] ptr; + delete [] comments; + delete [] npIds; + + return false; + } + else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND) + { + app.DebugPrintf("[LeaderboardManager] getScoreByIds(): Game ranking not found.\n"); + + ret = sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + + delete [] ptr; + delete [] comments; + delete [] npIds; + + m_scores = NULL; + m_readCount = 0; + m_maxRank = num; + + m_eStatsState = eStatsState_Ready; + return false; + } + else if (ret<0) goto error3; + + // Return. + sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + + m_readCount = num; + + // Filter scorers and construct output structure. + if (m_scores != NULL) delete [] m_scores; + m_scores = new ReadScore[m_readCount]; + convertToOutput(m_readCount, m_scores, ptr, comments); + m_maxRank = m_readCount; + + app.DebugPrintf( + "[LeaderboardManager] getScoreByIds(), Success!\n" + "\t Board %i\n" + "\t %i of %i results have an entry\n" + "1stScore=%i\n", + boardId, + m_readCount, num, + ptr->rankData.scoreValue + ); + + // Sort scores + std::sort(m_scores, m_scores + m_readCount, SortByRank); + + delete [] ptr; + delete [] comments; + delete [] npIds; + + m_eStatsState = eStatsState_Ready; + return true; + + // Error. +error3: + if (ret!=SCE_NP_COMMUNITY_ERROR_ABORTED) //0x8002a109 + sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + delete [] ptr; + delete [] comments; +error2: + if (npIds != NULL) delete [] npIds; +error1: + if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed; + app.DebugPrintf("[LeaderboardManger] getScoreByIds() FAILED, ret=0x%X\n", ret); + return false; +} + +bool PS3LeaderboardManager::getScoreByRange() +{ + CellRtcTick last_sort_date; + SceNpScoreRankNumber mTotalRecord; + + unsigned int num = m_readCount; + SceNpScoreRankData *ptr; + SceNpScoreComment *comments; + + assert(m_eFilterMode == eFM_TopRank); + + int ret = sceNpScoreCreateTransactionCtx(m_titleContext); + if (m_eStatsState == eStatsState_Canceled) + { + // Cancel operation has been called, abort. + app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() - m_eStatsState == eStatsState_Canceled.\n"); + sceNpScoreDestroyTransactionCtx(ret); + return false; + } + else if (ret < 0) + { + // Error occurred creating a transacion, abort. + m_eStatsState = eStatsState_Failed; + app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() - createTransaction failed, ret=0x%X\n", ret); + return false; + } + else + { + // Transaction created successfully, continue. + m_transactionCtx = ret; + app.DebugPrintf("[LeaderboardManager] REQUEST ID B = 0x%X\n", m_transactionCtx ); + } + + ptr = new SceNpScoreRankData[num]; + comments = new SceNpScoreComment[num]; + + int boardId = getBoardId(m_difficulty, m_statsType); + ret = sceNpScoreGetRankingByRange( + m_transactionCtx, + boardId, // BoardId + + m_startIndex, + + ptr, sizeof(SceNpScoreRankData) * num, //OUT: Rank Data + + comments, sizeof(SceNpScoreComment) * num, //OUT: Comment Data + + NULL, 0, // GameData. + + num, + + &last_sort_date, + &m_maxRank, // 'Total number of players registered in the target scoreboard.' + + NULL // Reserved, specify null. + ); + + if (ret == SCE_NP_COMMUNITY_ERROR_ABORTED) + { + app.DebugPrintf("[LeaderboardManager] getScoreByRange(): 'sceNpScoreGetRankingByRange' aborted (0x%X).\n", ret); + + ret = sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + + delete [] ptr; + delete [] comments; + + return false; + } + else if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_GAME_RANKING_NOT_FOUND) + { + app.DebugPrintf("[LeaderboardManager] getScoreByRange(): Game ranking not found."); + + ret = sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + + delete [] ptr; + delete [] comments; + + m_scores = NULL; + m_readCount = 0; + + m_eStatsState = eStatsState_Ready; + return false; + } + else if (ret<0) goto error2; + else + { + app.DebugPrintf("[LeaderboardManager] getScoreByRange(), success, 1stScore=%i.\n", ptr->scoreValue); + } + + // Return. + sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + + //m_stats = ptr; //Maybe: addPadding(num,ptr); + + if (m_scores != NULL) delete [] m_scores; + m_readCount = ret; + m_scores = new ReadScore[m_readCount]; + for (int i=0; i<m_readCount; i++) + { + //memcpy(m_scores+i, ptr+i, sizeof(SceNpScoreRankData)); + initReadScoreStruct(m_scores[i], ptr[i]); + //fromBase32(m_scores+i, comments+i); + fillReadScoreStruct(m_scores[i], comments[i]); + } + + m_eStatsState = eStatsState_Ready; + return true; + + // Error. +error2: + if (ret!=SCE_NP_COMMUNITY_ERROR_ABORTED) //0x8002a109 + sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + + delete [] ptr; + delete [] comments; +error1: + if (m_eStatsState != eStatsState_Canceled) m_eStatsState = eStatsState_Failed; + app.DebugPrintf("[LeaderboardManager]\tgetScoreByRange() failed, ret=0x%X\n", ret); + return false; +} + +bool PS3LeaderboardManager::setScore() +{ + int ret; + SceNpId npId; + int32_t writeTitleContext = 0; + SceNpScoreRankNumber tmp = 0; + SceNpScoreComment comment; + + // Get next job. + + EnterCriticalSection(&m_csViewsLock); + RegisterScore rscore = m_views.front(); + m_views.pop(); + LeaveCriticalSection(&m_csViewsLock); + + if ( ProfileManager.IsGuest(rscore.m_iPad) ) + { + app.DebugPrintf("[LeaderboardManager] setScore(): m_iPad[%i] is guest.\n", rscore.m_iPad); + return true; + } + + ProfileManager.GetSceNpId(rscore.m_iPad, &npId); + writeTitleContext = sceNpScoreCreateTitleCtx( &s_npCommunicationId, &s_npCommunicationPassphrase, &npId); + if (writeTitleContext < 0) + { + app.DebugPrintf("[LeaderboardManager] setScore(): sceNpScoreCreateTitleCtx FAILED, ret == %X.\n", ret); + return false; + } + + ret = sceNpScoreCreateTransactionCtx(writeTitleContext); + + // Start emptying queue if leaderboards has been closed. + if (ret == SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED) + { + EnterCriticalSection(&m_csViewsLock); + m_views.pop(); + LeaveCriticalSection(&m_csViewsLock); + } + + // Error handling. + if (ret<0) + { + app.DebugPrintf("[LeaderboardManager] setScore() FAILED, ret=0x%X\n", ret); + sceNpScoreDestroyTransactionCtx(writeTitleContext); + return false; + } + m_transactionCtx = ret; + app.DebugPrintf("[LeaderboardManager] REQUEST ID C = 0x%X\n", m_transactionCtx ); + + toBase32(&comment, (void *) &rscore.m_commentData); + + int boardId = getBoardId(rscore.m_difficulty, rscore.m_commentData.m_statsType); + ret = sceNpScoreRecordScore( + m_transactionCtx, // transId, + boardId, // boardId, + rscore.m_score, //IN: new score, + + &comment, // Comments + NULL, // GameInfo + + &tmp, //OUT: current rank, + + NULL // Reserved, specify null. + ); + + if (ret==SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE) //0x8002A415 + { + app.DebugPrintf("[LeaderboardManager] setScore(), doesn't beat current score, %i.\n", tmp); + } + else if (ret==SCE_NP_COMMUNITY_ERROR_ABORTED) goto error1;//0x8002a109 + else if (ret<0) goto error2; + else + { + app.DebugPrintf("[LeaderboardManager] setScore(), success. boardId=%i, score=%i\n", boardId, rscore.m_score); + } + + // Return. + sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; + //m_eStatsState = eStatsState_Idle; + return true; + + // Error. +error2: + sceNpScoreDestroyTransactionCtx(m_transactionCtx); + m_transactionCtx = 0; +error1: + app.DebugPrintf("[LeaderboardManager] setScore() FAILED, ret=0x%X\n", ret); + sceNpScoreDestroyTitleCtx(writeTitleContext); + return false; +} + +void PS3LeaderboardManager::Tick() +{ + ReadView view; + + switch( m_eStatsState ) + { + case eStatsState_Ready: + { + 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 = eStatsReturn_NoResults; + if (view.m_numQueries > 0) + ret = eStatsReturn_Success; + + if (m_readListener != NULL) + { + app.DebugPrintf("[LeaderboardManager] OnStatsReadComplete(%i, %i, _), m_readCount=%i.\n", ret, m_maxRank, m_readCount); + m_readListener->OnStatsReadComplete(ret, m_maxRank, view); + } + + m_eStatsState = eStatsState_Idle; + + delete [] m_scores; + m_scores = NULL; + } + break; + + case eStatsState_Failed: + { + view.m_numQueries = 0; + view.m_queries = NULL; + + if ( m_readListener != NULL ) + m_readListener->OnStatsReadComplete(eStatsReturn_NetworkError, 0, view); + + m_eStatsState = eStatsState_Idle; + } + break; + + case eStatsState_Canceled: + { + m_eStatsState = eStatsState_Idle; + } + break; + + default: // Getting or Idle. + break; + } +} + +bool PS3LeaderboardManager::OpenSession() +{ + if (m_openSessions == 0) + { + if (m_threadScoreboard == NULL) + { + m_threadScoreboard = new C4JThread(&scoreboardThreadEntry, this, "4JScoreboard"); + m_threadScoreboard->SetProcessor(CPU_CORE_LEADERBOARDS); + m_threadScoreboard->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); + m_threadScoreboard->Run(); + } + + app.DebugPrintf("[LeaderboardManager] OpenSession(): Starting sceNpScore utility.\n"); + } + else + { + app.DebugPrintf("[LeaderboardManager] OpenSession(): Another session opened, total=%i\n", m_openSessions+1); + } + + m_openSessions++; + return true; +} + +void PS3LeaderboardManager::CloseSession() +{ + m_openSessions--; + + if (m_openSessions == 0) app.DebugPrintf("[LeaderboardManager] CloseSession(): Quitting sceNpScore utility.\n"); + else app.DebugPrintf("[LeaderboardManager] CloseSession(): %i sessions still open.\n", m_openSessions); +} + +void PS3LeaderboardManager::DeleteSession() {} + +bool PS3LeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views) +{ + // Need to cancel read/write operation first. + //if (m_eStatsState != eStatsState_Idle) return false; + + // Write relevant parameters. + //RegisterScore *regScore = reinterpret_cast<RegisterScore *>(views); + + EnterCriticalSection(&m_csViewsLock); + for (int i=0; i<viewCount; i++) + { + app.DebugPrintf("[LeaderboardManager] WriteStats(), starting. difficulty=%i, statsType=%i, score=%i\n", + views[i].m_difficulty, views[i].m_commentData.m_statsType, views[i].m_score); + + m_views.push(views[i]); + } + LeaveCriticalSection(&m_csViewsLock); + + delete [] views; //*regScore; + + //m_eStatsState = eStatsState_Writing; + return true; +} + +// myUID ignored on PS3. +bool PS3LeaderboardManager::ReadStats_Friends(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount) +{ + // Need to cancel read/write operation first. + if (m_eStatsState != eStatsState_Idle) return false; + if (!LeaderboardManager::ReadStats_Friends(listener, difficulty, type, myUID, startIndex, readCount)) return false; + + //int ret = sceNpManagerGetNpId(&m_myNpId); + //if (ret<0) return false; + + m_eStatsState = eStatsState_Getting; + return true; +} + +// myUID ignored on PS3. +bool PS3LeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *listener, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount) +{ + // Need to cancel read/write operation first. + if (m_eStatsState != eStatsState_Idle) return false; + if (!LeaderboardManager::ReadStats_MyScore(listener, difficulty, type, myUID, readCount)) return false; + + //int ret = sceNpManagerGetNpId(&m_myNpId); + //if (ret<0) return false; + + m_eStatsState = eStatsState_Getting; + return true; +} + +// myUID ignored on PS3. +bool PS3LeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount) +{ + // Need to cancel read/write operation first. + if (m_eStatsState != eStatsState_Idle) return false; + if (!LeaderboardManager::ReadStats_TopRank(listener, difficulty, type, startIndex, readCount)) return false; + m_eStatsState = eStatsState_Getting; + return true; +} + +void PS3LeaderboardManager::FlushStats() {} + +void PS3LeaderboardManager::CancelOperation() +{ + m_readListener = NULL; + m_eStatsState = eStatsState_Canceled; + + 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"); + } +} + +bool PS3LeaderboardManager::isIdle() +{ + return m_eStatsState == eStatsState_Idle; +} + +int PS3LeaderboardManager::getBoardId(int difficulty, EStatsType statsType) +{ + switch (statsType) + { + case eStatsType_Travelling: + if (0 <= difficulty && difficulty < 4) return 1 + difficulty; // [1,2,3,4] + else return -1; + + case eStatsType_Mining: + if (0 <= difficulty && difficulty < 4) return 5 + difficulty; // [5,6,7,8] + else return -1; + + case eStatsType_Farming: + if (0 <= difficulty && difficulty < 4) return 9 + difficulty; // [9,10,11,12] + else return -1; + + case eStatsType_Kills: + if (1 <= difficulty && difficulty < 4) return 13 + difficulty - 1; // [13,14,15,16] + else return -1; + + default: return -1; + } +} + +// 4J-JEV: Filter out all friends who don't have scores. +/* +SceNpScoreRankData *PS3LeaderboardManager::filterJustScorers(unsigned int &num, SceNpScorePlayerRankData *friendsData) +{ + int num2 = 0; + for (int i=0; i<num; i++) if (friendsData[i].hasData) num2++; + num = num2; num2 = 0; + SceNpScoreRankData *out = new SceNpScoreRankData[num]; + for (int i=0; i<num; i++) if (friendsData[i].hasData) out[num2++] = friendsData[i].rankData; + return out; +} */ + +// 4J-JEV: Unused, here if we want to switch LeaderboardManager::ViewOut to 'SceNpScorePlayerRankData'. +SceNpScorePlayerRankData *PS3LeaderboardManager::addPadding(unsigned int num, SceNpScoreRankData *rankData) +{ + SceNpScorePlayerRankData *out = new SceNpScorePlayerRankData[num]; + for (int i=0; i<num; i++) + { + out[i].hasData = true; + out[i].rankData = rankData[i]; + } + delete [] rankData; + return out; +} + +// 4J-JEV: Filter and create output object. +void PS3LeaderboardManager::convertToOutput(unsigned int &num, ReadScore *out, SceNpScorePlayerRankData *rankData, SceNpScoreComment *comm) +{ + int j=0; + for (int i=0; i<num; i++) + { + if (rankData[i].hasData) + { + initReadScoreStruct( out[j], rankData[i].rankData ); + fillReadScoreStruct( out[j], comm[i] ); + + j++; + } + } + + num = j; +} + +void PS3LeaderboardManager::toBinary(void *out, SceNpScoreComment *in) +{ + string decoded = base64_decode( string((char*)in) ); + memcpy(out, decoded.c_str(), RECORD_SIZE); +} + +void PS3LeaderboardManager::fromBinary(SceNpScoreComment **out, void *in) +{ + *out = (SceNpScoreComment *) new unsigned char[SCE_NP_SCORE_COMMENT_MAXLEN + 1]; + string encoded = base64_encode((unsigned char const *) in, RECORD_SIZE); + memcpy(out, encoded.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN); +} + +void PS3LeaderboardManager::toBase32(SceNpScoreComment *out, void *in) +{ + ZeroMemory(out,sizeof(SceNpScoreComment)); + PBYTE bytes = (PBYTE) in; + char *chars = out->data; + + for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++) + { + int sByte = (i*5) / 8; + int eByte = (5+(i*5)) / 8; + int dIndex = (i*5) % 8; + + unsigned char fivebits = 0; + + fivebits = *(bytes+sByte) << dIndex; + + if (eByte != sByte) + fivebits = fivebits | *(bytes+eByte) >> (8-dIndex); + + fivebits = (fivebits>>3) & 0x1F; + + if (fivebits < 10) // 0 - 9 + chars[i] = '0' + fivebits; + else if (fivebits < 32) // A - V + chars[i] = 'A' + (fivebits-10); + else + assert(false); + } + + toSymbols(out->data); +} + +void PS3LeaderboardManager::fromBase32(void *out, SceNpScoreComment *in) +{ + PBYTE bytes = (PBYTE) out; + ZeroMemory(bytes, RECORD_SIZE); + + fromSymbols(in->data); + + char ch[2] = { 0, 0 }; + for (int i = 0; i < SCE_NP_SCORE_COMMENT_MAXLEN; i++) + { + ch[0] = in->data[i]; + unsigned char fivebits = strtol(ch, NULL, 32) << 3; + + int sByte = (i*5) / 8; + int eByte = (5+(i*5)) / 8; + int dIndex = (i*5) % 8; + + *(bytes + sByte) = *(bytes+sByte) | (fivebits >> dIndex); + + if (eByte != sByte) + *(bytes + eByte) = fivebits << (8-dIndex); + } +} + +char symbBase32[32] = { + ' ', '!','\"', '#', '$', '%', '&','\'', '(', ')', + '*', '+', '`', '-', '.', '/', ':', ';', '<', '=', + '>', '?', '[','\\', ']', '^', '_', '{', '|', '}', + '~', '@' +}; + +char charBase32[32] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V' +}; + +void PS3LeaderboardManager::toSymbols(char *str) +{ + for (int i = 0; i < 63; i++) + { + for (int j=0; j < 32; j++) + { + if (str[i]==charBase32[j]) + str[i] =symbBase32[j]; + } + } +} + +void PS3LeaderboardManager::fromSymbols(char *str) +{ + for (int i = 0; i < 63; i++) + { + for (int j=0; j < 32; j++) + { + if (str[i]==symbBase32[j]) + str[i] =charBase32[j]; + } + } +} + +bool PS3LeaderboardManager::test_string(string testing) +{ +#ifndef _CONTENT_PACKAGE + static SceNpScoreComment comment; + ZeroMemory(&comment, sizeof(SceNpScoreComment)); + memcpy(&comment, testing.c_str(), SCE_NP_SCORE_COMMENT_MAXLEN); + + int ctx = sceNpScoreCreateTransactionCtx(m_titleContext); + if (ctx<0) return false; + + int ret = sceNpScoreCensorComment(ctx, (const void *) &comment, NULL); + + if (ret == SCE_NP_COMMUNITY_SERVER_ERROR_CENSORED) + { + //app.DebugPrintf("\n[TEST_STRING]: REJECTED "); + } + else if (ret < 0) + { + sceNpScoreDestroyTransactionCtx(ctx); + return false; + } + else + { + //app.DebugPrintf("\n[TEST_STRING]: permitted "); + } + + //app.DebugPrintf("'%s'\n", comment.data); + sceNpScoreDestroyTransactionCtx(ctx); + return true; +#else + return true; +#endif +} + +void PS3LeaderboardManager::initReadScoreStruct(ReadScore &out, SceNpScoreRankData &rankData) +{ + ZeroMemory(&out, sizeof(ReadScore)); + + // Init rank and onlineID + out.m_uid.setOnlineID( rankData.npId.handle, true ); + out.m_rank = rankData.rank; + + // Convert to wstring and copy name. + wstring wstrName = convStringToWstring( string(rankData.onlineName.data) ).c_str(); + //memcpy(&out.m_name, wstrName.c_str(), XUSER_NAME_SIZE); + out.m_name=wstrName; +} + +void PS3LeaderboardManager::fillReadScoreStruct(ReadScore &out, SceNpScoreComment &comment) +{ + StatsData statsData; + fromBase32( (void *) &statsData, &comment ); + + switch (statsData.m_statsType) + { + case eStatsType_Farming: + out.m_statsSize = 6; + out.m_statsData[0] = statsData.m_farming.m_eggs; + out.m_statsData[1] = statsData.m_farming.m_wheat; + out.m_statsData[2] = statsData.m_farming.m_mushroom; + out.m_statsData[3] = statsData.m_farming.m_sugarcane; + out.m_statsData[4] = statsData.m_farming.m_milk; + out.m_statsData[5] = statsData.m_farming.m_pumpkin; + break; + case eStatsType_Mining: + out.m_statsSize = 7; + out.m_statsData[0] = statsData.m_mining.m_dirt; + out.m_statsData[1] = statsData.m_mining.m_cobblestone; + out.m_statsData[2] = statsData.m_mining.m_sand; + out.m_statsData[3] = statsData.m_mining.m_stone; + out.m_statsData[4] = statsData.m_mining.m_gravel; + out.m_statsData[5] = statsData.m_mining.m_clay; + out.m_statsData[6] = statsData.m_mining.m_obsidian; + break; + case eStatsType_Kills: + out.m_statsSize = 7; + out.m_statsData[0] = statsData.m_kills.m_zombie; + out.m_statsData[1] = statsData.m_kills.m_skeleton; + out.m_statsData[2] = statsData.m_kills.m_creeper; + out.m_statsData[3] = statsData.m_kills.m_spider; + out.m_statsData[4] = statsData.m_kills.m_spiderJockey; + out.m_statsData[5] = statsData.m_kills.m_zombiePigman; + out.m_statsData[6] = statsData.m_kills.m_slime; + break; + case eStatsType_Travelling: + out.m_statsSize = 4; + out.m_statsData[0] = statsData.m_travelling.m_walked; + out.m_statsData[1] = statsData.m_travelling.m_fallen; + out.m_statsData[2] = statsData.m_travelling.m_minecart; + out.m_statsData[3] = statsData.m_travelling.m_boat; + break; + } +} + +bool PS3LeaderboardManager::SortByRank(const ReadScore &lhs, const ReadScore &rhs) +{ + return lhs.m_rank < rhs.m_rank; +}
\ No newline at end of file diff --git a/Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.h b/Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.h new file mode 100644 index 00000000..7a85896d --- /dev/null +++ b/Minecraft.Client/PS3/Leaderboards/PS3LeaderboardManager.h @@ -0,0 +1,110 @@ +#pragma once + +#include "..\..\Common\Leaderboards\LeaderboardManager.h" +#include "..\..\..\Minecraft.World\x64headers\extraX64.h" + +#include "PS3\Passphrase\ps3__np_conf.h" + +using namespace std; + +class PS3LeaderboardManager : public LeaderboardManager +{ +protected: + enum EStatsState + { + eStatsState_Idle, + eStatsState_Getting, + eStatsState_Failed, + eStatsState_Ready, + eStatsState_Canceled, + //eStatsState_Writing, + eStatsState_Max + }; + +public: + PS3LeaderboardManager(); + virtual ~PS3LeaderboardManager(); + +private: + unsigned short m_openSessions; + + C4JThread *m_threadScoreboard; + bool m_running; + + int32_t m_titleContext; + int m_transactionCtx; + + //SceNpId m_myNpId; + + static int scoreboardThreadEntry(LPVOID lpParam); + void scoreboardThreadInternal(); + + bool getScoreByIds(); + bool getScoreByRange(); + + bool setScore(); + queue<RegisterScore> m_views; + + CRITICAL_SECTION m_csViewsLock; + + EStatsState m_eStatsState; //State of the stats read +// EFilterMode m_eFilterMode; + + ReadScore *m_scores; + unsigned int m_maxRank; + //SceNpScoreRankData *m_stats; + +public: + 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(); + +private: + int getBoardId(int difficulty, EStatsType); + + //LeaderboardManager::ReadScore *filterJustScorers(unsigned int &num, LeaderboardManager::ReadScore *friendsData); + + SceNpScorePlayerRankData *addPadding(unsigned int num, SceNpScoreRankData *rankData); + + void convertToOutput(unsigned int &num, ReadScore *out, SceNpScorePlayerRankData *rankData, SceNpScoreComment *comm); + + void toBinary(void *out, SceNpScoreComment *in); + void fromBinary(SceNpScoreComment **out, void *in); + + void toBase32(SceNpScoreComment *out, void *in); + void fromBase32(void *out, SceNpScoreComment *in); + + void toSymbols(char *); + void fromSymbols(char *); + + bool test_string(string); + + void initReadScoreStruct(ReadScore &out, SceNpScoreRankData &); + void fillReadScoreStruct(ReadScore &out, SceNpScoreComment &comment); + + static bool SortByRank(const ReadScore &lhs, const ReadScore &rhs); +}; diff --git a/Minecraft.Client/PS3/Leaderboards/base64.cpp b/Minecraft.Client/PS3/Leaderboards/base64.cpp new file mode 100644 index 00000000..19106cc4 --- /dev/null +++ b/Minecraft.Client/PS3/Leaderboards/base64.cpp @@ -0,0 +1,131 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "stdafx.h" + +#include "base64.h" +#include <iostream> + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +// 4J ADDED, +std::string base64_encode(std::string str) +{ + return base64_encode( reinterpret_cast<const unsigned char*>(str.c_str()), str.length() ); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(int ii = 0; (ii <4) ; ii++) + ret += base64_chars[char_array_4[ii]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +}
\ No newline at end of file diff --git a/Minecraft.Client/PS3/Leaderboards/base64.h b/Minecraft.Client/PS3/Leaderboards/base64.h new file mode 100644 index 00000000..7f6a1e49 --- /dev/null +++ b/Minecraft.Client/PS3/Leaderboards/base64.h @@ -0,0 +1,7 @@ +#pragma once + +#include <string> + +std::string base64_encode(std::string str); +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s);
\ No newline at end of file |
