4coder/test_data/lots_of_files/gjScore.h

352 lines
18 KiB
C++

///////////////////////////////////////////////////////////////////
//*-------------------------------------------------------------*//
//| Part of the Game Jolt API C++ Library (http://gamejolt.com) |//
//*-------------------------------------------------------------*//
//| Released under the zlib License |//
//| More information available in the readme file |//
//*-------------------------------------------------------------*//
///////////////////////////////////////////////////////////////////
#pragma once
#ifndef _GJ_GUARD_SCORE_H_
#define _GJ_GUARD_SCORE_H_
// ****************************************************************
/*! Score table object class.\n
* http://gamejolt.com/api/doc/game/scores/
* \brief Score Table Object */
class gjScoreTable final
{
private:
int m_iID; //!< ID of the score table
std::string m_sTitle; //!< title/name of the score table
std::string m_sDescription; //!< description text of the score table
int m_iSortDir; //!< sort direction of the score table (see #GJ_SORT_DIRECTION)
bool m_bPrimary; //!< primary status
static gjScoreTable* s_pPrimary; //!< pointer to the primary score table
std::vector<gjScore*> m_apScore; //!< semi-cached score entries
std::vector<gjScore*> m_apVerify; //!< verification list with temporary score entries (helper)
gjAPI* m_pAPI; //!< main interface access pointer
public:
gjScoreTable(const gjData& aScoreTableData, gjAPI* pAPI)noexcept;
~gjScoreTable();
/*! \name Fetch Scores Request */
//! @{
/*! Fetch and semi-cache specific score entries of this score table through an API request.
* \pre Login maybe required
* \note \b -Now blocks, \b -Call uses non-blocking callbacks
* \param bOnlyUser Fetch only score entries from the current main user (Login required)
* \param iLimit Max number of fetched score entries
* \return **GJ_OK** on success\n
* **GJ_REQUEST_FAILED** if request was unsuccessful\n
* **GJ_NOT_CONNECTED** if connection/login is missing\n
* **GJ_NO_DATA_FOUND** if no scores were found\n
* (see #GJ_ERROR) */
inline int FetchScoresNow(const bool& bOnlyUser, const int& iLimit, gjScoreList* papOutput) {if(!papOutput) return GJ_INVALID_INPUT; return this->__FetchScores(bOnlyUser, iLimit, papOutput, GJ_NETWORK_NULL_API(gjScoreList));}
template <typename T> inline int FetchScoresCall(const bool& bOnlyUser, const int& iLimit, GJ_NETWORK_OUTPUT(gjScoreList)) {return this->__FetchScores(bOnlyUser, iLimit, NULL, GJ_NETWORK_OUTPUT_FW);}
//! @}
/*! \name Add Score Request */
//! @{
/*! Add a new score entry to this score table through an API request.\n
* May does nothing, if score entry was already added (compares name and sort).\n
* May does nothing, if main user has a better highscore.
* \pre Login maybe required
* \note \b -Now blocks, \b -Call uses non-blocking callbacks
* \param sScore Score string
* \param iSort Numerical sort value of the score
* \param sExtraData Extra data associated with the score
* \param sGuestName Name of a guest user (leave empty to use the current main user, Login required)
* \return **GJ_OK** on success\n
* **GJ_REQUEST_FAILED** if request was unsuccessful\n
* **GJ_REQUEST_CANCELED** if score entry was already added or if main user has a better highscore\n
* **GJ_INVALID_INPUT** if score string is empty or sort value is zero\n
* **GJ_NOT_CONNECTED** if connection/login is missing\n
* (see #GJ_ERROR) */
inline int AddScoreNow(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName) {return this->__AddScore(sScore, iSort, sExtraData, sGuestName, true, GJ_NETWORK_NULL_API(gjScorePtr));}
inline int AddScoreCall(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName) {return this->__AddScore(sScore, iSort, sExtraData, sGuestName, false, GJ_NETWORK_NULL_API(gjScorePtr));}
template <typename T> inline int AddScoreCall(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName, GJ_NETWORK_OUTPUT(gjScorePtr)) {return this->__AddScore(sScore, iSort, sExtraData, sGuestName, false, GJ_NETWORK_OUTPUT_FW);}
//! @}
/*! \name Add Score Request Base64 */
//! @{
/*! Like \link AddScoreNow AddScore\endlink\n
* Allows to send extra data in binary form.
* \pre Login maybe required
* \note \b -Now blocks, \b -Call uses non-blocking callbacks
* \param sScore Score string
* \param iSort Numerical sort value of the score
* \param pExtraData Extra data in binary form associated with the score
* \param iExtraSize Size of the extra data
* \param sGuestName Name of a guest user (leave empty to use the current main user, Login required)
* \return **GJ_OK** on success\n
* **GJ_REQUEST_FAILED** if request was unsuccessful\n
* **GJ_REQUEST_CANCELED** if score entry was already added or if main user has a better highscore\n
* **GJ_INVALID_INPUT** if score string is empty or sort value is zero\n
* **GJ_NOT_CONNECTED** if connection/login is missing\n
* (see #GJ_ERROR) */
inline int AddScoreBase64Now(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName) {return this->__AddScoreBase64(sScore, iSort, pExtraData, iExtraSize, sGuestName, true, GJ_NETWORK_NULL_API(gjScorePtr));}
inline int AddScoreBase64Call(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName) {return this->__AddScoreBase64(sScore, iSort, pExtraData, iExtraSize, sGuestName, false, GJ_NETWORK_NULL_API(gjScorePtr));}
template <typename T> inline int AddScoreBase64Call(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName, GJ_NETWORK_OUTPUT(gjScorePtr)) {return this->__AddScoreBase64(sScore, iSort, pExtraData, iExtraSize, sGuestName, false, GJ_NETWORK_OUTPUT_FW);}
//! @}
/*! \name Get Attributes */
//! @{
inline const int& GetID()const {return m_iID;} //!< \copybrief m_iID
inline const std::string& GetTitle()const {return m_sTitle;} //!< \copybrief m_sTitle
inline const std::string& GetDescription()const {return m_sDescription;} //!< \copybrief m_sDescription
inline const int& GetSortDirection()const {return m_iSortDir;} //!< \copybrief m_iSortDir
/*! */ //! @}
/*! \name Get Static Attributes */
//! @{
static inline gjScoreTable* GetPrimary() {return s_pPrimary;} //!< \copybrief s_pPrimary
/*! */ //! @}
/*! \name Check Status */
//! @{
inline bool IsPrimary()const {return m_bPrimary;} //!< \copybrief m_bPrimary
//! @}
private:
DISABLE_COPY(gjScoreTable)
/*! \name Superior Request Functions */
//! @{
template <typename T> int __FetchScores(const bool& bOnlyUser, const int& iLimit, gjScoreList* papOutput, GJ_NETWORK_OUTPUT(gjScoreList));
template <typename T> int __AddScore(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr));
template <typename T> int __AddScoreBase64(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr));
//! @}
/*! \name Management Functions */
//! @{
int __CheckCache(const bool bOnlyUser, const int& iLimit, gjScoreList* papOutput);
int __Process(const std::string& sData, void* pAdd, gjScoreList* papOutput);
//! @}
/*! \name Callback Functions */
//! @{
int __AddScoreCallback(const std::string& sData, void* pAdd, gjScorePtr* pOutput);
//! @}
};
// ****************************************************************
/*! Score entry class.\n
* http://gamejolt.com/api/doc/game/scores/
* \brief Score Entry Object */
class gjScore final
{
private:
std::string m_sScore; //!< score string
int m_iSort; //!< numerical sort value of the score
std::string m_sExtraData; //!< extra data associated with the score
int m_iUserID; //!< ID of the user, 0 if guest
std::string m_sUserName; //!< display name of the user or guest
std::string m_sDate; //!< time string when the score was logged (e.g. 4 weeks ago)
gjScoreTable* m_pScoreTable; //!< associated score table
gjAPI* m_pAPI; //!< main interface access pointer
public:
gjScore(const gjData& aScoreData, gjScoreTable* pScoreTable, gjAPI* pAPI)noexcept;
/*! \name Fetch User Request */
//! @{
/*! Fetch and cache the associated user through an API request.
* \note \b -Now blocks, \b -Call uses non-blocking callbacks
* \return **GJ_OK** on success\n
* **GJ_REQUEST_FAILED** if request was unsuccessful\n
* **GJ_NOT_CONNECTED** if connection/login is missing\n
* (see #GJ_ERROR) */
inline int FetchUserNow(gjUserPtr* ppOutput) {return m_pAPI->InterUser()->FetchUserNow(this->IsGuest() ? -1 : m_iUserID, ppOutput);}
template <typename T> inline int FetchUserCall(GJ_NETWORK_OUTPUT(gjUserPtr)) {return m_pAPI->InterUser()->FetchUserCall(this->IsGuest() ? -1 : m_iUserID, GJ_NETWORK_OUTPUT_FW);}
//! @}
/*! \name Get Attributes */
//! @{
inline const std::string& GetScore()const {return m_sScore;} //!< \copybrief m_sScore
inline const int& GetSort()const {return m_iSort;} //!< \copybrief m_iSort
inline const std::string& GetExtraData()const {return m_sExtraData;} //!< \copybrief m_sExtraData
inline const int& GetUserID()const {return m_iUserID;} //!< \copybrief m_iUserID
inline const std::string& GetUserName()const {return m_sUserName;} //!< \copybrief m_sUserName
inline const std::string& GetDate()const {return m_sDate;} //!< \copybrief m_sDate
inline gjScoreTable* GetScoreTable()const {return m_pScoreTable;} //!< \copybrief m_pScoreTable
/*! */ //! @}
/*! \name Get Base64 Attributes */
//! @{
inline int GetExtraDataBase64(void* pTarget, const size_t& iSize)const {if(!pTarget || iSize <= 0) return GJ_INVALID_INPUT; base64_decode(m_sExtraData.c_str(), (unsigned char*)pTarget, iSize); return GJ_OK;} //!< \copybrief m_sExtraData
/*! */ //! @}
/*! \name Check Status */
//! @{
inline bool IsGuest()const {return m_iUserID ? false : true;} //!< guest status
//! @}
private:
DISABLE_COPY(gjScore)
};
// ****************************************************************
/*! \name Sorting Callbacks */
//! @{
bool SortAscending(const gjScore* i, const gjScore* j);
bool SortDescending(const gjScore* i, const gjScore* j);
//! @}
// ****************************************************************
/* fetch and semi-cache specific score entries of this score table */
template <typename T> int gjScoreTable::__FetchScores(const bool& bOnlyUser, const int& iLimit, gjScoreList* papOutput, GJ_NETWORK_OUTPUT(gjScoreList))
{
if(!m_pAPI->IsUserConnected() && bOnlyUser) return GJ_NOT_CONNECTED;
if(iLimit <= 0) return GJ_INVALID_INPUT;
const bool bNow = papOutput ? true : false;
if(bOnlyUser && m_iSortDir)
{
// check for cached score entries
gjScoreList apCache;
if(this->__CheckCache(bOnlyUser, iLimit, &apCache) == GJ_OK)
{
if(bNow) (*papOutput) = apCache;
else (pOutputObj->*OutputCallback)(apCache, pOutputData);
return GJ_OK;
}
}
// only scores from the main user or all scores
const std::string sUserData = bOnlyUser ?
"&username=" + m_pAPI->GetProcUserName() +
"&user_token=" + m_pAPI->GetProcUserToken() :
"";
// send get score request
std::string sResponse;
if(m_pAPI->SendRequest("/scores/"
"?game_id=" + m_pAPI->GetProcGameID() +
"&table_id=" + gjAPI::UtilIntToString(m_iID) +
"&limit=" + gjAPI::UtilIntToString(iLimit) +
sUserData, bNow ? &sResponse : NULL, this, &gjScoreTable::__Process, NULL, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED;
if(bNow) return this->__Process(sResponse, NULL, papOutput);
return GJ_OK;
}
// ****************************************************************
/* add score entry to this score table */
template <typename T> int gjScoreTable::__AddScore(const std::string& sScore, const int& iSort, const std::string& sExtraData, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr))
{
if(sScore == "" || iSort == 0) return GJ_INVALID_INPUT;
const bool bGuest = (sGuestName != "") ? true : false;
if(!m_pAPI->IsUserConnected() && !bGuest) return GJ_NOT_CONNECTED;
if(m_iSortDir && !bGuest)
{
// check for cached best score and cancel request
gjScoreList apBestScore;
if(this->__CheckCache(true, 1, &apBestScore) == GJ_OK)
{
if(((m_iSortDir == GJ_SORT_DESC) && (iSort <= apBestScore[0]->GetSort())) ||
((m_iSortDir == GJ_SORT_ASC) && (iSort >= apBestScore[0]->GetSort())))
return GJ_REQUEST_CANCELED;
}
}
// check if specific score entry is already available and cancel request
const std::string sUserName = bGuest ? sGuestName : m_pAPI->GetUserName();
FOR_EACH(it, m_apScore)
{
gjScore* pScore = (*it);
if((pScore->GetUserID() == -1) == bGuest &&
pScore->GetUserName() == sUserName &&
pScore->GetSort() == iSort)
{
// specific score entry already available
return GJ_REQUEST_CANCELED;
}
}
// create temporary score entry
gjData asScoreData;
if(!bGuest)
{
// fetch cached main user
gjUserPtr pMainUser;
const int iError = m_pAPI->InterUser()->FetchUserNow(0, &pMainUser);
if(iError) return iError;
asScoreData["user_id"] = gjAPI::UtilIntToString(pMainUser->GetID());
asScoreData["user"] = pMainUser->GetName();
}
asScoreData["score"] = sScore;
asScoreData["sort"] = gjAPI::UtilIntToString(iSort);
asScoreData["extra_data"] = sExtraData;
asScoreData["stored"] = GJ_API_TEXT_NOW;
asScoreData["guest"] = sGuestName;
// add score entry to verification list
gjScore* pVerify = new gjScore(asScoreData, this, m_pAPI);
m_apVerify.push_back(pVerify);
// use user data or guest name
const std::string sUserData = bGuest ?
"&guest=" + gjAPI::UtilEscapeString(sGuestName) :
"&username=" + m_pAPI->GetProcUserName() +
"&user_token=" + m_pAPI->GetProcUserToken();
// send add score request
std::string sResponse;
if(m_pAPI->SendRequest("/scores/add/"
"?game_id=" + m_pAPI->GetProcGameID() +
"&table_id=" + gjAPI::UtilIntToString(m_iID) +
"&score=" + gjAPI::UtilEscapeString(sScore) +
"&sort=" + gjAPI::UtilIntToString(iSort) +
"&extra_data=" + gjAPI::UtilEscapeString(sExtraData) +
sUserData, bNow ? &sResponse : NULL, this, &gjScoreTable::__AddScoreCallback, pVerify, GJ_NETWORK_OUTPUT_FW)) return GJ_REQUEST_FAILED;
if(bNow) return this->__AddScoreCallback(sResponse, pVerify, NULL);
return GJ_OK;
}
// ****************************************************************
/* add score entry to this score table with Base64 extra data */
template <typename T> int gjScoreTable::__AddScoreBase64(const std::string& sScore, const int& iSort, void* pExtraData, const size_t& iExtraSize, const std::string& sGuestName, const bool& bNow, GJ_NETWORK_OUTPUT(gjScorePtr))
{
if(!pExtraData || iExtraSize <= 0) return GJ_INVALID_INPUT;
const size_t iNeed = base64_needed(iExtraSize);
// convert binary data to Base64 string
char* pcBase64 = new char[iNeed];
base64_encode((unsigned char*)pExtraData, iExtraSize, pcBase64, iNeed);
// execute add score function with Base64 string
const int iReturn = this->__AddScore(sScore, iSort, pcBase64, sGuestName, bNow, GJ_NETWORK_OUTPUT_FW);
SAFE_DELETE_ARRAY(pcBase64)
return iReturn;
}
#endif /* _GJ_GUARD_SCORE_H_ */