Skip to content

Commit

Permalink
Handle Pausing in AchievementManager
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
LillyJadeKatrin committed Jun 15, 2024
1 parent 3c346ab commit 263d5d0
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 4 deletions.
44 changes: 42 additions & 2 deletions Source/Core/Core/AchievementManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ void AchievementManager::Init()
LoadDefaultBadges();
if (!m_client && Config::Get(Config::RA_ENABLED))
{
m_client = rc_client_create(MemoryPeeker, Request);
{
std::lock_guard lg{m_client_lock};
m_client = rc_client_create(MemoryPeeker, 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());
Expand Down Expand Up @@ -157,6 +160,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()
Expand Down Expand Up @@ -243,6 +249,39 @@ 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) / 60),
OSD::Duration::VERY_LONG, OSD::Color::RED);
}
return can_pause;
}

void AchievementManager::DoIdle()
{
m_idle_thread = std::make_unique<std::thread>([]() {
while (Core::GetState(*AchievementManager::GetInstance().m_system) == Core::State::Paused &&
AchievementManager::GetInstance().m_background_execution_allowed)
{
Common::SleepCurrentThread(1000);
std::lock_guard lg{AchievementManager::GetInstance().m_client_lock};
auto* client = AchievementManager::GetInstance().m_client;
if (!client || !AchievementManager::GetInstance().IsGameLoaded())
return;
rc_client_idle(client);
INFO_LOG_FMT(ACHIEVEMENTS, "Beep");
}
});
m_idle_thread->detach();
}

std::recursive_mutex& AchievementManager::GetLock()
{
return m_lock;
Expand Down Expand Up @@ -441,8 +480,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();
Expand All @@ -458,6 +497,7 @@ void AchievementManager::Shutdown()
if (m_client)
{
CloseGame();
std::lock_guard lg{m_client_lock};
m_queue.Shutdown();
// DON'T log out - keep those credentials for next run.
rc_client_destroy(m_client);
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/Core/AchievementManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ class AchievementManager

void DoFrame();

bool CanPause();
void DoIdle();

std::recursive_mutex& GetLock();
void SetHardcoreMode();
bool IsHardcoreModeActive() const;
Expand Down Expand Up @@ -193,6 +196,7 @@ class AchievementManager
Badge m_default_game_badge;
Badge m_default_unlocked_badge;
Badge m_default_locked_badge;
bool m_background_execution_allowed = true;
Badge m_player_badge;
Hash m_game_hash{};
u32 m_game_id = 0;
Expand All @@ -204,6 +208,7 @@ class AchievementManager
std::unordered_map<AchievementId, Badge> m_locked_badges;
RichPresence m_rich_presence;
std::chrono::steady_clock::time_point m_last_rp_time = std::chrono::steady_clock::now();
std::unique_ptr<std::thread> m_idle_thread = nullptr;
std::chrono::steady_clock::time_point m_last_progress_message = std::chrono::steady_clock::now();

std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
Expand All @@ -214,6 +219,7 @@ class AchievementManager
Common::WorkQueueThread<std::function<void()>> m_queue;
Common::WorkQueueThread<std::function<void()>> m_image_queue;
mutable std::recursive_mutex m_lock;
mutable std::recursive_mutex m_client_lock;
std::recursive_mutex m_filereader_lock;
}; // class AchievementManager

Expand Down
10 changes: 9 additions & 1 deletion Source/Core/Core/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 (!IsRunningAndStarted())
Expand All @@ -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:
{
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/Core/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,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();
Expand Down

0 comments on commit 263d5d0

Please sign in to comment.