Skip to content

Commit

Permalink
[PR 127] Retrolayer: Add Achievements
Browse files Browse the repository at this point in the history
  • Loading branch information
Shardul555 authored and garbear committed Jan 27, 2023
1 parent fc0f6a3 commit 6116b66
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 2 deletions.
69 changes: 69 additions & 0 deletions xbmc/addons/kodi-dev-kit/include/kodi/addon-instance/Game.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,20 @@ class ATTR_DLL_LOCAL CInstanceGame : public IAddonInstance
return GAME_ERROR_NOT_IMPLEMENTED;
}

//============================================================================
/// @brief Set the credentials of the RetroAchievements user
///
/// @param[in] username The RetroAchievements username of the user
/// @param[in] token The login token to RetroAchievements of the user
///
/// @return the error, or @ref GAME_ERROR_NO_ERROR if the call was successful
///
virtual GAME_ERROR SetRetroAchievementsCredentials(const std::string& username,
const std::string& token)
{
return GAME_ERROR_NOT_IMPLEMENTED;
}

//============================================================================
/// @brief Gets a URL to the endpoint that updates the rich presence
/// in the user's RetroAchievements profile
Expand Down Expand Up @@ -993,6 +1007,7 @@ class ATTR_DLL_LOCAL CInstanceGame : public IAddonInstance
/// @param[in] size The size of the evaluation char pointer
/// @param[in] consoleID The console ID as it is defined by rcheevos for
/// the console the rom is made for
///
/// @return the error, or @ref GAME_ERROR_NO_ERROR if the evaluation was
/// created successfully
///
Expand All @@ -1001,6 +1016,32 @@ class ATTR_DLL_LOCAL CInstanceGame : public IAddonInstance
return GAME_ERROR_NOT_IMPLEMENTED;
}

//============================================================================
/// @brief Activate an achievement
///
/// @param[in] cheevoId The achievement ID
/// @param[in] memaddr Pointer to game memory
///
/// @return the error, or @ref GAME_ERROR_NO_ERROR if the call was successful
///
virtual GAME_ERROR ActivateAchievement(unsigned cheevoId, const char* memaddr)
{
return GAME_ERROR_NOT_IMPLEMENTED;
}

//============================================================================
/// @brief Get the achievement URL and ID
///
/// @param[in] callback A callback that receives the URL and ID
///
/// @return the error, or @ref GAME_ERROR_NO_ERROR if the call was successful
///
virtual GAME_ERROR GetCheevo_URL_ID(void (*callback)(const char* achievementUrl,
unsigned cheevoId))
{
return GAME_ERROR_NOT_IMPLEMENTED;
}

//============================================================================
/// @brief Resets the runtime. Must be called each time a new rom is starting
/// and when the savestate is changed
Expand Down Expand Up @@ -1052,9 +1093,13 @@ class ATTR_DLL_LOCAL CInstanceGame : public IAddonInstance
instance->game->toAddon->RCGenerateHashFromFile = ADDON_RCGenerateHashFromFile;
instance->game->toAddon->RCGetGameIDUrl = ADDON_RCGetGameIDUrl;
instance->game->toAddon->RCGetPatchFileUrl = ADDON_RCGetPatchFileUrl;
instance->game->toAddon->SetRetroAchievementsCredentials =
ADDON_SetRetroAchievementsCredentials;
instance->game->toAddon->RCPostRichPresenceUrl = ADDON_RCPostRichPresenceUrl;
instance->game->toAddon->RCEnableRichPresence = ADDON_RCEnableRichPresence;
instance->game->toAddon->RCGetRichPresenceEvaluation = ADDON_RCGetRichPresenceEvaluation;
instance->game->toAddon->ActivateAchievement = ADDON_ActivateAchievement;
instance->game->toAddon->GetCheevo_URL_ID = ADDON_GetCheevo_URL_ID;
instance->game->toAddon->RCResetRuntime = ADDON_RCResetRuntime;

instance->game->toAddon->FreeString = ADDON_FreeString;
Expand Down Expand Up @@ -1301,6 +1346,14 @@ class ATTR_DLL_LOCAL CInstanceGame : public IAddonInstance
return ret;
}

inline static GAME_ERROR ADDON_SetRetroAchievementsCredentials(const AddonInstance_Game* instance,
const char* username,
const char* token)
{
return static_cast<CInstanceGame*>(instance->toAddon->addonInstance)
->SetRetroAchievementsCredentials(username, token);
}

inline static GAME_ERROR ADDON_RCPostRichPresenceUrl(const AddonInstance_Game* instance,
char** url,
char** postData,
Expand Down Expand Up @@ -1354,6 +1407,22 @@ class ATTR_DLL_LOCAL CInstanceGame : public IAddonInstance
return ret;
}

inline static GAME_ERROR ADDON_ActivateAchievement(const AddonInstance_Game* instance,
unsigned cheevoId,
const char* memaddr)
{
return static_cast<CInstanceGame*>(instance->toAddon->addonInstance)
->ActivateAchievement(cheevoId, memaddr);
}

inline static GAME_ERROR ADDON_GetCheevo_URL_ID(const AddonInstance_Game* instance,
void (*callback)(const char* achievementUrl,
unsigned cheevoId))
{
return static_cast<CInstanceGame*>(instance->toAddon->addonInstance)
->GetCheevo_URL_ID(callback);
}

inline static GAME_ERROR ADDON_RCResetRuntime(const AddonInstance_Game* instance)
{
return static_cast<CInstanceGame*>(instance->toAddon->addonInstance)->RCResetRuntime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,8 @@ extern "C"
GAME_ERROR(__cdecl* RCGetGameIDUrl)(const AddonInstance_Game*, char**, const char*);
GAME_ERROR(__cdecl* RCGetPatchFileUrl)
(const AddonInstance_Game*, char**, const char*, const char*, unsigned int);
GAME_ERROR(__cdecl* SetRetroAchievementsCredentials)
(const AddonInstance_Game*, const char*, const char*);
GAME_ERROR(__cdecl* RCPostRichPresenceUrl)
(const AddonInstance_Game*,
char**,
Expand All @@ -1207,6 +1209,9 @@ extern "C"
GAME_ERROR(__cdecl* RCEnableRichPresence)(const AddonInstance_Game*, const char*);
GAME_ERROR(__cdecl* RCGetRichPresenceEvaluation)
(const AddonInstance_Game*, char**, unsigned int);
GAME_ERROR(__cdecl* ActivateAchievement)(const AddonInstance_Game*, unsigned int, const char*);
GAME_ERROR(__cdecl* GetCheevo_URL_ID)
(const AddonInstance_Game*, void (*)(const char*, unsigned int));
GAME_ERROR(__cdecl* RCResetRuntime)(const AddonInstance_Game*);
void(__cdecl* FreeString)(const AddonInstance_Game*, char*);
} KodiToAddonFuncTable_Game;
Expand Down
4 changes: 2 additions & 2 deletions xbmc/addons/kodi-dev-kit/include/kodi/versions.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@
#define ADDON_INSTANCE_VERSION_AUDIOENCODER_DEPENDS "c-api/addon-instance/audioencoder.h" \
"addon-instance/AudioEncoder.h"

#define ADDON_INSTANCE_VERSION_GAME "3.0.0"
#define ADDON_INSTANCE_VERSION_GAME_MIN "3.0.0"
#define ADDON_INSTANCE_VERSION_GAME "4.0.0"
#define ADDON_INSTANCE_VERSION_GAME_MIN "4.0.0"
#define ADDON_INSTANCE_VERSION_GAME_XML_ID "kodi.binary.instance.game"
#define ADDON_INSTANCE_VERSION_GAME_DEPENDS "addon-instance/Game.h"

Expand Down
2 changes: 2 additions & 0 deletions xbmc/cores/RetroPlayer/RetroPlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options

m_cheevos->EnableRichPresence();

m_cheevos->ActivateAchievement();

// Initialize gameplay
CreatePlayback(savestatePath);
RegisterWindowCallbacks();
Expand Down
69 changes: 69 additions & 0 deletions xbmc/cores/RetroPlayer/cheevos/Cheevos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include "utils/Variant.h"
#include "utils/log.h"
#include "vector"
#include "xbmc/dialogs/GUIDialogKaiToast.h"

#include <unordered_map>
#include <vector>

using namespace KODI;
using namespace RETRO;
Expand All @@ -30,14 +34,24 @@ constexpr auto GAME_ID = "GameID";
constexpr auto PATCH_DATA = "PatchData";
constexpr auto RICH_PRESENCE = "RichPresencePatch";

constexpr auto ACHIEVEMENTS = "Achievements";
constexpr auto MEM_ADDR = "MemAddr";
constexpr auto CHEEVO_ID = "ID";
constexpr auto FLAGS = "Flags";
constexpr auto CHEEVO_TITLE = "Title";
constexpr auto BADGE_NAME = "BadgeName";

constexpr int RESPORNSE_SIZE = 64;
} // namespace

std::unordered_map<unsigned, std::vector<std::string>> CCheevos::m_activatedCheevoMap;

CCheevos::CCheevos(GAME::CGameClient* gameClient,
const std::string& userName,
const std::string& loginToken)
: m_gameClient(gameClient), m_userName(userName), m_loginToken(loginToken)
{
m_gameClient->Cheevos().SetRetroAchievementsCredentials(m_userName.c_str(), m_loginToken.c_str());
}

void CCheevos::ResetRuntime()
Expand Down Expand Up @@ -108,6 +122,22 @@ bool CCheevos::LoadData()
m_richPresenceScript = data[PATCH_DATA][RICH_PRESENCE].asString();
m_richPresenceLoaded = true;

const CVariant& achievements = data[PATCH_DATA][ACHIEVEMENTS];
for (auto it = achievements.begin_array(); it != achievements.end_array(); it++)
{
const CVariant& achievement = *it;
if (achievement[FLAGS].asUnsignedInteger() == 3)
{
m_activatedCheevoMap[achievement[CHEEVO_ID].asUnsignedInteger()] = {
achievement[MEM_ADDR].asString(), achievement[CHEEVO_TITLE].asString(),
achievement[BADGE_NAME].asString()};
}
else
{
CLog::Log(LOGINFO, "We are not considering unofficial achievements");
}
}

return true;
}

Expand All @@ -126,6 +156,28 @@ void CCheevos::EnableRichPresence()
m_richPresenceScript.clear();
}

void CCheevos::ActivateAchievement()
{
if (m_activatedCheevoMap.empty())
{
if (!LoadData())
{
CLog::Log(LOGERROR, "Cheevos: Couldn't load patch file");
return;
}
else
{
CLog::Log(LOGERROR, "No active core achievement for the game");
}
}
for (auto& it : m_activatedCheevoMap)
{
m_gameClient->Cheevos().ActivateAchievement(it.first, it.second[0].c_str());
}
//call for checking triggered achievement
CheckTriggeredAchievement();
}

std::string CCheevos::GetRichPresenceEvaluation()
{
if (!m_richPresenceLoaded)
Expand Down Expand Up @@ -160,3 +212,20 @@ RConsoleID CCheevos::ConsoleID()

return it->second;
}

void CCheevos::Callback_URL_ID(const char* achievementUrl, unsigned int cheevoId)
{
XFILE::CCurlFile curl;
std::string res;
curl.Get(achievementUrl, res);
std::string description = m_activatedCheevoMap[cheevoId][1];
std::string header = std::string("Congratulations, ") + std::string("Achievement Unlocked");

CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, header, description);
}

void CCheevos::CheckTriggeredAchievement()
{
// Callback for triggered achievement URL and ID
m_gameClient->Cheevos().GetAchievement_URL_ID(Callback_URL_ID);
}
8 changes: 8 additions & 0 deletions xbmc/cores/RetroPlayer/cheevos/Cheevos.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include <map>
#include <string>
#include <unordered_map>
#include <vector>

namespace KODI
{
Expand All @@ -32,6 +34,12 @@ class CCheevos
void EnableRichPresence();
std::string GetRichPresenceEvaluation();

void ActivateAchievement();
static void Callback_URL_ID(const char* achievementUrl, unsigned int cheevoId);
void CheckTriggeredAchievement();

static std::unordered_map<unsigned, std::vector<std::string>> m_activatedCheevoMap;

private:
bool LoadData();
RConsoleID ConsoleID();
Expand Down
48 changes: 48 additions & 0 deletions xbmc/games/addons/cheevos/GameClientCheevos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,22 @@ bool CGameClientCheevos::RCPostRichPresenceUrl(std::string& url,
return error == GAME_ERROR_NO_ERROR;
}

void CGameClientCheevos::SetRetroAchievementsCredentials(const std::string& username,
const std::string& token)
{
GAME_ERROR error = GAME_ERROR_NO_ERROR;
try
{
m_gameClient.LogError(error = m_struct.toAddon->SetRetroAchievementsCredentials(
&m_struct, username.c_str(), token.c_str()),
"SetRetroAchievementsCredentials");
}
catch (...)
{
m_gameClient.LogException("SetRetroAchievementsCredentials");
}
}

void CGameClientCheevos::RCEnableRichPresence(const std::string& script)
{
GAME_ERROR error = GAME_ERROR_NO_ERROR;
Expand Down Expand Up @@ -176,6 +192,38 @@ void CGameClientCheevos::RCGetRichPresenceEvaluation(std::string& evaluation,
}
}

void CGameClientCheevos::ActivateAchievement(unsigned cheevoId, const char* memaddr)
{
GAME_ERROR error = GAME_ERROR_NO_ERROR;

try
{
m_gameClient.LogError(error =
m_struct.toAddon->ActivateAchievement(&m_struct, cheevoId, memaddr),
"ActivateAchievement()");
}
catch (...)
{
m_gameClient.LogException("ActivateAchievement()");
}
}

void CGameClientCheevos::GetAchievement_URL_ID(void (*callback)(const char* achievementUrl,
unsigned cheevoId))
{
GAME_ERROR error = GAME_ERROR_NO_ERROR;

try
{
m_gameClient.LogError(error = m_struct.toAddon->GetCheevo_URL_ID(&m_struct, callback),
"GetCheevo_ID_URL()");
}
catch (...)
{
m_gameClient.LogException("GetCheevoID()");
}
}

void CGameClientCheevos::RCResetRuntime()
{
GAME_ERROR error = GAME_ERROR_NO_ERROR;
Expand Down
5 changes: 5 additions & 0 deletions xbmc/games/addons/cheevos/GameClientCheevos.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class CGameClientCheevos
const std::string& username,
const std::string& token,
unsigned int gameID);
void SetRetroAchievementsCredentials(const std::string& username, const std::string& token);
bool RCPostRichPresenceUrl(std::string& url,
std::string& postData,
const std::string& username,
Expand All @@ -46,6 +47,10 @@ class CGameClientCheevos
const std::string& richPresence);
void RCEnableRichPresence(const std::string& script);
void RCGetRichPresenceEvaluation(std::string& evaluation, RETRO::RConsoleID consoleID);

void ActivateAchievement(unsigned cheevoId, const char* memaddr);
void GetAchievement_URL_ID(void (*callback)(const char* achievementUrl, unsigned cheevoId));

// When the game is reset, the runtime should also be reset
void RCResetRuntime();

Expand Down

0 comments on commit 6116b66

Please sign in to comment.