Handle Pausing in AchievementManager
There are two pieces of functionality to be added here. One, we want to disallow pausing too frequently, as it may be used as an artificial slowdown. This is handled within the client, which can tell us if a pause is allowed. Two, we want to call rc_client_idle on a periodic basis so the connection with the server can be maintained even while the emulator is paused.
This commit is contained in:
parent
01b44837f4
commit
33081184bb
|
@ -24,6 +24,7 @@
|
|||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/System.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
|
@ -46,7 +47,10 @@ void AchievementManager::Init()
|
|||
LoadDefaultBadges();
|
||||
if (!m_client && Config::Get(Config::RA_ENABLED))
|
||||
{
|
||||
m_client = rc_client_create(MemoryVerifier, Request);
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_client = rc_client_create(MemoryVerifier, Request);
|
||||
}
|
||||
std::string host_url = Config::Get(Config::RA_HOST_URL);
|
||||
if (!host_url.empty())
|
||||
rc_client_set_host(m_client, host_url.c_str());
|
||||
|
@ -160,6 +164,9 @@ bool AchievementManager::IsGameLoaded() const
|
|||
|
||||
void AchievementManager::SetBackgroundExecutionAllowed(bool allowed)
|
||||
{
|
||||
m_background_execution_allowed = allowed;
|
||||
if (allowed && Core::GetState(*AchievementManager::GetInstance().m_system) == Core::State::Paused)
|
||||
DoIdle();
|
||||
}
|
||||
|
||||
void AchievementManager::FetchPlayerBadge()
|
||||
|
@ -246,6 +253,54 @@ void AchievementManager::DoFrame()
|
|||
}
|
||||
}
|
||||
|
||||
bool AchievementManager::CanPause()
|
||||
{
|
||||
u32 frames_to_next_pause = 0;
|
||||
bool can_pause = rc_client_can_pause(m_client, &frames_to_next_pause);
|
||||
if (!can_pause)
|
||||
{
|
||||
OSD::AddMessage("Cannot spam pausing in hardcore mode.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
OSD::AddMessage(
|
||||
fmt::format("Can pause in {} seconds.",
|
||||
static_cast<float>(frames_to_next_pause) /
|
||||
Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate()),
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
}
|
||||
return can_pause;
|
||||
}
|
||||
|
||||
void AchievementManager::DoIdle()
|
||||
{
|
||||
std::thread([this]() {
|
||||
while (true)
|
||||
{
|
||||
Common::SleepCurrentThread(1000);
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
if (!m_system || Core::GetState(*m_system) != Core::State::Paused)
|
||||
return;
|
||||
if (!m_background_execution_allowed)
|
||||
return;
|
||||
if (!m_client || !IsGameLoaded())
|
||||
return;
|
||||
}
|
||||
// rc_client_idle peeks at memory to recalculate rich presence and therefore
|
||||
// needs to be on host or CPU thread to access memory.
|
||||
Core::QueueHostJob([this](Core::System& system) {
|
||||
std::lock_guard lg{m_lock};
|
||||
if (!m_system || Core::GetState(*m_system) != Core::State::Paused)
|
||||
return;
|
||||
if (!m_background_execution_allowed)
|
||||
return;
|
||||
if (!m_client || !IsGameLoaded())
|
||||
return;
|
||||
rc_client_idle(m_client);
|
||||
});
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
std::recursive_mutex& AchievementManager::GetLock()
|
||||
{
|
||||
return m_lock;
|
||||
|
@ -444,8 +499,8 @@ void AchievementManager::CloseGame()
|
|||
void AchievementManager::Logout()
|
||||
{
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
CloseGame();
|
||||
std::lock_guard lg{m_lock};
|
||||
m_player_badge.width = 0;
|
||||
m_player_badge.height = 0;
|
||||
m_player_badge.data.clear();
|
||||
|
@ -462,6 +517,7 @@ void AchievementManager::Shutdown()
|
|||
{
|
||||
CloseGame();
|
||||
m_queue.Shutdown();
|
||||
std::lock_guard lg{m_lock};
|
||||
// DON'T log out - keep those credentials for next run.
|
||||
rc_client_destroy(m_client);
|
||||
m_client = nullptr;
|
||||
|
|
|
@ -103,6 +103,9 @@ public:
|
|||
|
||||
void DoFrame();
|
||||
|
||||
bool CanPause();
|
||||
void DoIdle();
|
||||
|
||||
std::recursive_mutex& GetLock();
|
||||
void SetHardcoreMode();
|
||||
bool IsHardcoreModeActive() const;
|
||||
|
@ -194,6 +197,7 @@ private:
|
|||
Badge m_default_game_badge;
|
||||
Badge m_default_unlocked_badge;
|
||||
Badge m_default_locked_badge;
|
||||
std::atomic_bool m_background_execution_allowed = true;
|
||||
Badge m_player_badge;
|
||||
Hash m_game_hash{};
|
||||
u32 m_game_id = 0;
|
||||
|
|
|
@ -358,7 +358,7 @@ static void CPUSetInitialExecutionState(bool force_paused = false)
|
|||
// SetState must be called on the host thread, so we defer it for later.
|
||||
QueueHostJob([force_paused](Core::System& system) {
|
||||
bool paused = SConfig::GetInstance().bBootToPause || force_paused;
|
||||
SetState(system, paused ? State::Paused : State::Running);
|
||||
SetState(system, paused ? State::Paused : State::Running, true, true);
|
||||
Host_UpdateDisasmDialog();
|
||||
Host_UpdateMainFrame();
|
||||
Host_Message(HostMessageID::WMUserCreate);
|
||||
|
@ -698,7 +698,8 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
|
||||
// Set or get the running state
|
||||
|
||||
void SetState(Core::System& system, State state, bool report_state_change)
|
||||
void SetState(Core::System& system, State state, bool report_state_change,
|
||||
bool initial_execution_state)
|
||||
{
|
||||
// State cannot be controlled until the CPU Thread is operational
|
||||
if (s_state.load() != State::Running)
|
||||
|
@ -707,11 +708,18 @@ void SetState(Core::System& system, State state, bool report_state_change)
|
|||
switch (state)
|
||||
{
|
||||
case State::Paused:
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (!initial_execution_state && !AchievementManager::GetInstance().CanPause())
|
||||
return;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
// NOTE: GetState() will return State::Paused immediately, even before anything has
|
||||
// stopped (including the CPU).
|
||||
system.GetCPU().EnableStepping(true); // Break
|
||||
Wiimote::Pause();
|
||||
ResetRumble();
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().DoIdle();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
break;
|
||||
case State::Running:
|
||||
{
|
||||
|
|
|
@ -148,7 +148,8 @@ bool IsHostThread();
|
|||
bool WantsDeterminism();
|
||||
|
||||
// [NOT THREADSAFE] For use by Host only
|
||||
void SetState(Core::System& system, State state, bool report_state_change = true);
|
||||
void SetState(Core::System& system, State state, bool report_state_change = true,
|
||||
bool initial_execution_state = false);
|
||||
State GetState(Core::System& system);
|
||||
|
||||
void SaveScreenShot();
|
||||
|
|
Loading…
Reference in New Issue