Merge pull request #12856 from LillyJadeKatrin/retroachievements-pause-v2
Handle Pausing in AchievementManager
This commit is contained in:
commit
5ea3d9fca0
|
@ -6,6 +6,7 @@ import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.utils.ActivityTracker;
|
||||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||||
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
|
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
|
||||||
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
|
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
|
||||||
|
@ -20,6 +21,7 @@ public class DolphinApplication extends Application
|
||||||
{
|
{
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
application = this;
|
application = this;
|
||||||
|
registerActivityLifecycleCallbacks(new ActivityTracker());
|
||||||
VolleyUtil.init(getApplicationContext());
|
VolleyUtil.init(getApplicationContext());
|
||||||
System.loadLibrary("main");
|
System.loadLibrary("main");
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package org.dolphinemu.dolphinemu.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application.ActivityLifecycleCallbacks
|
||||||
|
import android.os.Bundle
|
||||||
|
|
||||||
|
class ActivityTracker : ActivityLifecycleCallbacks {
|
||||||
|
val resumedActivities = HashSet<Activity>()
|
||||||
|
var backgroundExecutionAllowed = false
|
||||||
|
|
||||||
|
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
||||||
|
|
||||||
|
override fun onActivityStarted(activity: Activity) {}
|
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity) {
|
||||||
|
resumedActivities.add(activity)
|
||||||
|
if (!backgroundExecutionAllowed && !resumedActivities.isEmpty()) {
|
||||||
|
backgroundExecutionAllowed = true
|
||||||
|
setBackgroundExecutionAllowedNative(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityPaused(activity: Activity) {
|
||||||
|
resumedActivities.remove(activity)
|
||||||
|
if (backgroundExecutionAllowed && resumedActivities.isEmpty()) {
|
||||||
|
backgroundExecutionAllowed = false
|
||||||
|
setBackgroundExecutionAllowedNative(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStopped(activity: Activity) {}
|
||||||
|
|
||||||
|
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
|
||||||
|
|
||||||
|
override fun onActivityDestroyed(activity: Activity) {}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
external fun setBackgroundExecutionAllowedNative(allowed: Boolean)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2024 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Core/AchievementManager.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_utils_ActivityTracker_setBackgroundExecutionAllowedNative(
|
||||||
|
JNIEnv*, jclass, jboolean allowed)
|
||||||
|
{
|
||||||
|
// This is called with allowed == false when the app goes into the background.
|
||||||
|
// We use this to stop continuously running background threads so we don't waste battery.
|
||||||
|
|
||||||
|
INFO_LOG_FMT(CORE, "SetBackgroundExecutionAllowed {}", allowed);
|
||||||
|
AchievementManager::GetInstance().SetBackgroundExecutionAllowed(allowed);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
add_library(main SHARED
|
add_library(main SHARED
|
||||||
|
ActivityTracker.cpp
|
||||||
Cheats/ARCheat.cpp
|
Cheats/ARCheat.cpp
|
||||||
Cheats/Cheats.h
|
Cheats/Cheats.h
|
||||||
Cheats/GeckoCheat.cpp
|
Cheats/GeckoCheat.cpp
|
||||||
|
@ -11,6 +12,7 @@ add_library(main SHARED
|
||||||
GameList/GameFile.cpp
|
GameList/GameFile.cpp
|
||||||
GameList/GameFile.h
|
GameList/GameFile.h
|
||||||
GameList/GameFileCache.cpp
|
GameList/GameFileCache.cpp
|
||||||
|
GpuDriver.cpp
|
||||||
Host.cpp
|
Host.cpp
|
||||||
Host.h
|
Host.h
|
||||||
InfinityConfig.cpp
|
InfinityConfig.cpp
|
||||||
|
@ -32,7 +34,6 @@ add_library(main SHARED
|
||||||
RiivolutionPatches.cpp
|
RiivolutionPatches.cpp
|
||||||
SkylanderConfig.cpp
|
SkylanderConfig.cpp
|
||||||
WiiUtils.cpp
|
WiiUtils.cpp
|
||||||
GpuDriver.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(main
|
target_link_libraries(main
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "Core/Config/AchievementSettings.h"
|
#include "Core/Config/AchievementSettings.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
|
#include "Core/HW/VideoInterface.h"
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
#include "DiscIO/Blob.h"
|
#include "DiscIO/Blob.h"
|
||||||
|
@ -47,7 +48,10 @@ void AchievementManager::Init()
|
||||||
LoadDefaultBadges();
|
LoadDefaultBadges();
|
||||||
if (!m_client && Config::Get(Config::RA_ENABLED))
|
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);
|
std::string host_url = Config::Get(Config::RA_HOST_URL);
|
||||||
if (!host_url.empty())
|
if (!host_url.empty())
|
||||||
rc_client_set_host(m_client, host_url.c_str());
|
rc_client_set_host(m_client, host_url.c_str());
|
||||||
|
@ -159,6 +163,13 @@ bool AchievementManager::IsGameLoaded() const
|
||||||
return game_info && game_info->id != 0;
|
return game_info && game_info->id != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
void AchievementManager::FetchPlayerBadge()
|
||||||
{
|
{
|
||||||
FetchBadge(&m_player_badge, RC_IMAGE_TYPE_USER,
|
FetchBadge(&m_player_badge, RC_IMAGE_TYPE_USER,
|
||||||
|
@ -243,6 +254,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()
|
std::recursive_mutex& AchievementManager::GetLock()
|
||||||
{
|
{
|
||||||
return m_lock;
|
return m_lock;
|
||||||
|
@ -441,8 +500,8 @@ void AchievementManager::CloseGame()
|
||||||
void AchievementManager::Logout()
|
void AchievementManager::Logout()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard lg{m_lock};
|
|
||||||
CloseGame();
|
CloseGame();
|
||||||
|
std::lock_guard lg{m_lock};
|
||||||
m_player_badge.width = 0;
|
m_player_badge.width = 0;
|
||||||
m_player_badge.height = 0;
|
m_player_badge.height = 0;
|
||||||
m_player_badge.data.clear();
|
m_player_badge.data.clear();
|
||||||
|
@ -459,6 +518,7 @@ void AchievementManager::Shutdown()
|
||||||
{
|
{
|
||||||
CloseGame();
|
CloseGame();
|
||||||
m_queue.Shutdown();
|
m_queue.Shutdown();
|
||||||
|
std::lock_guard lg{m_lock};
|
||||||
// DON'T log out - keep those credentials for next run.
|
// DON'T log out - keep those credentials for next run.
|
||||||
rc_client_destroy(m_client);
|
rc_client_destroy(m_client);
|
||||||
m_client = nullptr;
|
m_client = nullptr;
|
||||||
|
|
|
@ -96,12 +96,16 @@ public:
|
||||||
bool HasAPIToken() const;
|
bool HasAPIToken() const;
|
||||||
void LoadGame(const std::string& file_path, const DiscIO::Volume* volume);
|
void LoadGame(const std::string& file_path, const DiscIO::Volume* volume);
|
||||||
bool IsGameLoaded() const;
|
bool IsGameLoaded() const;
|
||||||
|
void SetBackgroundExecutionAllowed(bool allowed);
|
||||||
|
|
||||||
void FetchPlayerBadge();
|
void FetchPlayerBadge();
|
||||||
void FetchGameBadges();
|
void FetchGameBadges();
|
||||||
|
|
||||||
void DoFrame();
|
void DoFrame();
|
||||||
|
|
||||||
|
bool CanPause();
|
||||||
|
void DoIdle();
|
||||||
|
|
||||||
std::recursive_mutex& GetLock();
|
std::recursive_mutex& GetLock();
|
||||||
void SetHardcoreMode();
|
void SetHardcoreMode();
|
||||||
bool IsHardcoreModeActive() const;
|
bool IsHardcoreModeActive() const;
|
||||||
|
@ -193,6 +197,7 @@ private:
|
||||||
Badge m_default_game_badge;
|
Badge m_default_game_badge;
|
||||||
Badge m_default_unlocked_badge;
|
Badge m_default_unlocked_badge;
|
||||||
Badge m_default_locked_badge;
|
Badge m_default_locked_badge;
|
||||||
|
std::atomic_bool m_background_execution_allowed = true;
|
||||||
Badge m_player_badge;
|
Badge m_player_badge;
|
||||||
Hash m_game_hash{};
|
Hash m_game_hash{};
|
||||||
u32 m_game_id = 0;
|
u32 m_game_id = 0;
|
||||||
|
@ -239,6 +244,8 @@ public:
|
||||||
|
|
||||||
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
|
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
|
||||||
|
|
||||||
|
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}
|
||||||
|
|
||||||
constexpr void DoFrame() {}
|
constexpr void DoFrame() {}
|
||||||
|
|
||||||
constexpr void CloseGame() {}
|
constexpr void CloseGame() {}
|
||||||
|
|
|
@ -353,7 +353,7 @@ static void CPUSetInitialExecutionState(bool force_paused = false)
|
||||||
// SetState must be called on the host thread, so we defer it for later.
|
// SetState must be called on the host thread, so we defer it for later.
|
||||||
QueueHostJob([force_paused](Core::System& system) {
|
QueueHostJob([force_paused](Core::System& system) {
|
||||||
bool paused = SConfig::GetInstance().bBootToPause || force_paused;
|
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_UpdateDisasmDialog();
|
||||||
Host_UpdateMainFrame();
|
Host_UpdateMainFrame();
|
||||||
Host_Message(HostMessageID::WMUserCreate);
|
Host_Message(HostMessageID::WMUserCreate);
|
||||||
|
@ -693,7 +693,8 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
||||||
|
|
||||||
// Set or get the running state
|
// 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
|
// State cannot be controlled until the CPU Thread is operational
|
||||||
if (s_state.load() != State::Running)
|
if (s_state.load() != State::Running)
|
||||||
|
@ -702,11 +703,18 @@ void SetState(Core::System& system, State state, bool report_state_change)
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case State::Paused:
|
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
|
// NOTE: GetState() will return State::Paused immediately, even before anything has
|
||||||
// stopped (including the CPU).
|
// stopped (including the CPU).
|
||||||
system.GetCPU().EnableStepping(true); // Break
|
system.GetCPU().EnableStepping(true); // Break
|
||||||
Wiimote::Pause();
|
Wiimote::Pause();
|
||||||
ResetRumble();
|
ResetRumble();
|
||||||
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
AchievementManager::GetInstance().DoIdle();
|
||||||
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
break;
|
break;
|
||||||
case State::Running:
|
case State::Running:
|
||||||
{
|
{
|
||||||
|
|
|
@ -143,7 +143,8 @@ bool IsHostThread();
|
||||||
bool WantsDeterminism();
|
bool WantsDeterminism();
|
||||||
|
|
||||||
// [NOT THREADSAFE] For use by Host only
|
// [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);
|
State GetState(Core::System& system);
|
||||||
|
|
||||||
void SaveScreenShot();
|
void SaveScreenShot();
|
||||||
|
|
Loading…
Reference in New Issue