Added Achievement Manager with Login
Added AchievementManager class. Upon startup (currently only in DolphinQt), logs into RetroAchievements with the login credentials stored in achievements.ini. Co-authored-by: AdmiralCurtiss <AdmiralCurtiss@users.noreply.github.com>
This commit is contained in:
parent
07d2f3d305
commit
84b3df0af2
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Common/HttpRequest.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Config/AchievementSettings.h"
|
||||
#include "Core/Core.h"
|
||||
|
||||
AchievementManager* AchievementManager::GetInstance()
|
||||
{
|
||||
static AchievementManager s_instance;
|
||||
return &s_instance;
|
||||
}
|
||||
|
||||
void AchievementManager::Init()
|
||||
{
|
||||
if (!m_is_runtime_initialized && Config::Get(Config::RA_ENABLED))
|
||||
{
|
||||
rc_runtime_init(&m_runtime);
|
||||
m_is_runtime_initialized = true;
|
||||
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
|
||||
LoginAsync("", [](ResponseType r_type) {});
|
||||
}
|
||||
}
|
||||
|
||||
AchievementManager::ResponseType AchievementManager::Login(const std::string& password)
|
||||
{
|
||||
return VerifyCredentials(password);
|
||||
}
|
||||
|
||||
void AchievementManager::LoginAsync(const std::string& password, const LoginCallback& callback)
|
||||
{
|
||||
m_queue.EmplaceItem([this, password, callback] { callback(VerifyCredentials(password)); });
|
||||
}
|
||||
|
||||
bool AchievementManager::IsLoggedIn() const
|
||||
{
|
||||
return m_login_data.response.succeeded;
|
||||
}
|
||||
|
||||
void AchievementManager::Logout()
|
||||
{
|
||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
|
||||
rc_api_destroy_login_response(&m_login_data);
|
||||
m_login_data.response.succeeded = 0;
|
||||
}
|
||||
|
||||
void AchievementManager::Shutdown()
|
||||
{
|
||||
m_is_runtime_initialized = false;
|
||||
m_queue.Shutdown();
|
||||
// DON'T log out - keep those credentials for next run.
|
||||
rc_api_destroy_login_response(&m_login_data);
|
||||
m_login_data.response.succeeded = 0;
|
||||
rc_runtime_destroy(&m_runtime);
|
||||
}
|
||||
|
||||
AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std::string& password)
|
||||
{
|
||||
std::string username = Config::Get(Config::RA_USERNAME);
|
||||
std::string api_token = Config::Get(Config::RA_API_TOKEN);
|
||||
rc_api_login_request_t login_request = {
|
||||
.username = username.c_str(), .api_token = api_token.c_str(), .password = password.c_str()};
|
||||
ResponseType r_type = Request<rc_api_login_request_t, rc_api_login_response_t>(
|
||||
login_request, &m_login_data, rc_api_init_login_request, rc_api_process_login_response);
|
||||
if (r_type == ResponseType::SUCCESS)
|
||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, m_login_data.api_token);
|
||||
return r_type;
|
||||
}
|
||||
|
||||
// Every RetroAchievements API call, with only a partial exception for fetch_image, follows
|
||||
// the same design pattern (here, X is the name of the call):
|
||||
// Create a specific rc_api_X_request_t struct and populate with the necessary values
|
||||
// Call rc_api_init_X_request to convert this into a generic rc_api_request_t struct
|
||||
// Perform the HTTP request using the url and post_data in the rc_api_request_t struct
|
||||
// Call rc_api_process_X_response to convert the raw string HTTP response into a
|
||||
// rc_api_X_response_t struct
|
||||
// Use the data in the rc_api_X_response_t struct as needed
|
||||
// Call rc_api_destroy_X_response when finished with the response struct to free memory
|
||||
template <typename RcRequest, typename RcResponse>
|
||||
AchievementManager::ResponseType AchievementManager::Request(
|
||||
RcRequest rc_request, RcResponse* rc_response,
|
||||
const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request,
|
||||
const std::function<int(RcResponse*, const char*)>& process_response)
|
||||
{
|
||||
rc_api_request_t api_request;
|
||||
Common::HttpRequest http_request;
|
||||
init_request(&api_request, &rc_request);
|
||||
auto http_response = http_request.Post(api_request.url, api_request.post_data);
|
||||
rc_api_destroy_request(&api_request);
|
||||
if (http_response.has_value() && http_response->size() > 0)
|
||||
{
|
||||
const std::string response_str(http_response->begin(), http_response->end());
|
||||
process_response(rc_response, response_str.c_str());
|
||||
if (rc_response->response.succeeded)
|
||||
{
|
||||
return ResponseType::SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logout();
|
||||
return ResponseType::INVALID_CREDENTIALS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ResponseType::CONNECTION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <rcheevos/include/rc_api_user.h>
|
||||
#include <rcheevos/include/rc_runtime.h>
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
enum class ResponseType
|
||||
{
|
||||
SUCCESS,
|
||||
INVALID_CREDENTIALS,
|
||||
CONNECTION_FAILED,
|
||||
UNKNOWN_FAILURE
|
||||
};
|
||||
using LoginCallback = std::function<void(ResponseType)>;
|
||||
|
||||
static AchievementManager* GetInstance();
|
||||
void Init();
|
||||
ResponseType Login(const std::string& password);
|
||||
void LoginAsync(const std::string& password, const LoginCallback& callback);
|
||||
bool IsLoggedIn() const;
|
||||
void Logout();
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
AchievementManager() = default;
|
||||
|
||||
ResponseType VerifyCredentials(const std::string& password);
|
||||
|
||||
template <typename RcRequest, typename RcResponse>
|
||||
ResponseType Request(RcRequest rc_request, RcResponse* rc_response,
|
||||
const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request,
|
||||
const std::function<int(RcResponse*, const char*)>& process_response);
|
||||
|
||||
rc_runtime_t m_runtime{};
|
||||
bool m_is_runtime_initialized = false;
|
||||
rc_api_login_response_t m_login_data{};
|
||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||
}; // class AchievementManager
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
|
@ -1,4 +1,6 @@
|
|||
add_library(core
|
||||
AchievementManager.cpp
|
||||
AchievementManager.h
|
||||
ActionReplay.cpp
|
||||
ActionReplay.h
|
||||
ARDecrypt.cpp
|
||||
|
|
|
@ -161,6 +161,7 @@
|
|||
<ClInclude Include="Common\WindowsRegistry.h" />
|
||||
<ClInclude Include="Common\WindowSystemInfo.h" />
|
||||
<ClInclude Include="Common\WorkQueueThread.h" />
|
||||
<ClInclude Include="Core\AchievementManager.h" />
|
||||
<ClInclude Include="Core\ActionReplay.h" />
|
||||
<ClInclude Include="Core\ARDecrypt.h" />
|
||||
<ClInclude Include="Core\Boot\Boot.h" />
|
||||
|
@ -796,6 +797,7 @@
|
|||
<ClCompile Include="Common\UPnP.cpp" />
|
||||
<ClCompile Include="Common\WindowsRegistry.cpp" />
|
||||
<ClCompile Include="Common\Version.cpp" />
|
||||
<ClCompile Include="Core\AchievementManager.cpp" />
|
||||
<ClCompile Include="Core\ActionReplay.cpp" />
|
||||
<ClCompile Include="Core\ARDecrypt.cpp" />
|
||||
<ClCompile Include="Core\Boot\Boot_BS2Emu.cpp" />
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "Common/Version.h"
|
||||
#include "Common/WindowSystemInfo.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/BootManager.h"
|
||||
#include "Core/CommonTitles.h"
|
||||
|
@ -222,6 +223,11 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
|
|||
|
||||
InitControllers();
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
// This has to be done before CreateComponents() so it's initialized.
|
||||
AchievementManager::GetInstance()->Init();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
CreateComponents();
|
||||
|
||||
ConnectGameList();
|
||||
|
@ -301,6 +307,10 @@ MainWindow::~MainWindow()
|
|||
Settings::Instance().ResetNetPlayClient();
|
||||
Settings::Instance().ResetNetPlayServer();
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance()->Shutdown();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
delete m_render_widget;
|
||||
delete m_netplay_dialog;
|
||||
|
||||
|
|
Loading…
Reference in New Issue