mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-11 06:59:07 +01:00
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
114
Source/Core/Core/AchievementManager.cpp
Normal file
114
Source/Core/Core/AchievementManager.cpp
Normal file
@ -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
|
54
Source/Core/Core/AchievementManager.h
Normal file
54
Source/Core/Core/AchievementManager.h
Normal file
@ -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
|
add_library(core
|
||||||
|
AchievementManager.cpp
|
||||||
|
AchievementManager.h
|
||||||
ActionReplay.cpp
|
ActionReplay.cpp
|
||||||
ActionReplay.h
|
ActionReplay.h
|
||||||
ARDecrypt.cpp
|
ARDecrypt.cpp
|
||||||
|
@ -161,6 +161,7 @@
|
|||||||
<ClInclude Include="Common\WindowsRegistry.h" />
|
<ClInclude Include="Common\WindowsRegistry.h" />
|
||||||
<ClInclude Include="Common\WindowSystemInfo.h" />
|
<ClInclude Include="Common\WindowSystemInfo.h" />
|
||||||
<ClInclude Include="Common\WorkQueueThread.h" />
|
<ClInclude Include="Common\WorkQueueThread.h" />
|
||||||
|
<ClInclude Include="Core\AchievementManager.h" />
|
||||||
<ClInclude Include="Core\ActionReplay.h" />
|
<ClInclude Include="Core\ActionReplay.h" />
|
||||||
<ClInclude Include="Core\ARDecrypt.h" />
|
<ClInclude Include="Core\ARDecrypt.h" />
|
||||||
<ClInclude Include="Core\Boot\Boot.h" />
|
<ClInclude Include="Core\Boot\Boot.h" />
|
||||||
@ -796,6 +797,7 @@
|
|||||||
<ClCompile Include="Common\UPnP.cpp" />
|
<ClCompile Include="Common\UPnP.cpp" />
|
||||||
<ClCompile Include="Common\WindowsRegistry.cpp" />
|
<ClCompile Include="Common\WindowsRegistry.cpp" />
|
||||||
<ClCompile Include="Common\Version.cpp" />
|
<ClCompile Include="Common\Version.cpp" />
|
||||||
|
<ClCompile Include="Core\AchievementManager.cpp" />
|
||||||
<ClCompile Include="Core\ActionReplay.cpp" />
|
<ClCompile Include="Core\ActionReplay.cpp" />
|
||||||
<ClCompile Include="Core\ARDecrypt.cpp" />
|
<ClCompile Include="Core\ARDecrypt.cpp" />
|
||||||
<ClCompile Include="Core\Boot\Boot_BS2Emu.cpp" />
|
<ClCompile Include="Core\Boot\Boot_BS2Emu.cpp" />
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
#include "Common/WindowSystemInfo.h"
|
#include "Common/WindowSystemInfo.h"
|
||||||
|
|
||||||
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/Boot/Boot.h"
|
#include "Core/Boot/Boot.h"
|
||||||
#include "Core/BootManager.h"
|
#include "Core/BootManager.h"
|
||||||
#include "Core/CommonTitles.h"
|
#include "Core/CommonTitles.h"
|
||||||
@ -222,6 +223,11 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
|
|||||||
|
|
||||||
InitControllers();
|
InitControllers();
|
||||||
|
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
// This has to be done before CreateComponents() so it's initialized.
|
||||||
|
AchievementManager::GetInstance()->Init();
|
||||||
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
CreateComponents();
|
CreateComponents();
|
||||||
|
|
||||||
ConnectGameList();
|
ConnectGameList();
|
||||||
@ -301,6 +307,10 @@ MainWindow::~MainWindow()
|
|||||||
Settings::Instance().ResetNetPlayClient();
|
Settings::Instance().ResetNetPlayClient();
|
||||||
Settings::Instance().ResetNetPlayServer();
|
Settings::Instance().ResetNetPlayServer();
|
||||||
|
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
AchievementManager::GetInstance()->Shutdown();
|
||||||
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
|
|
||||||
delete m_render_widget;
|
delete m_render_widget;
|
||||||
delete m_netplay_dialog;
|
delete m_netplay_dialog;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user