Skip to content

Commit 499c08a

Browse files
committed
Achievements: Warn on shutdown when unlocks are unconfirmed
1 parent ddcc2e0 commit 499c08a

File tree

5 files changed

+98
-46
lines changed

5 files changed

+98
-46
lines changed

src/core/achievements.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2327,6 +2327,15 @@ u32 Achievements::GetPauseThrottleFrames()
23272327
return rc_client_can_pause(s_state.client, &frames_remaining) ? 0 : frames_remaining;
23282328
}
23292329

2330+
u32 Achievements::GetPendingUnlockCount()
2331+
{
2332+
if (!IsActive())
2333+
return 0;
2334+
2335+
const auto lock = GetLock();
2336+
return rc_client_get_award_achievement_pending_count(s_state.client);
2337+
}
2338+
23302339
void Achievements::Logout()
23312340
{
23322341
if (IsActive())

src/core/achievements.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ bool DownloadGameIcons(ProgressCallback* progress, Error* error);
192192
/// Returns 0 if pausing is allowed, otherwise the number of frames until pausing is allowed.
193193
u32 GetPauseThrottleFrames();
194194

195+
/// Returns the number of unlocks that have not been synchronized with the server.
196+
u32 GetPendingUnlockCount();
197+
195198
/// The name of the RetroAchievements icon, which can be used in notifications.
196199
extern const char* const RA_LOGO_ICON_NAME;
197200

src/core/fullscreenui.cpp

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ static void DoStartFile();
8181
static void DoStartBIOS();
8282
static void DoStartDisc();
8383
static void DoToggleFastForward();
84-
static void ConfirmIfSavingMemoryCards(std::string action, std::function<void(bool)> callback);
84+
static void ConfirmWithSafetyCheck(std::string action, bool check_achievements, std::function<void(bool)> callback);
8585
static void RequestShutdown(bool save_state);
8686
static void RequestRestart();
8787
static void BeginChangeDiscOnCoreThread(bool needs_pause);
@@ -713,29 +713,45 @@ void FullscreenUI::DoStartDisc()
713713
});
714714
}
715715

716-
void FullscreenUI::ConfirmIfSavingMemoryCards(std::string action, std::function<void(bool)> callback)
716+
void FullscreenUI::ConfirmWithSafetyCheck(std::string action, bool check_achievements,
717+
std::function<void(bool)> callback)
717718
{
718-
Host::RunOnCoreThread([action = std::move(action), callback = std::move(callback)]() mutable {
719+
Host::RunOnCoreThread([action = std::move(action), callback = std::move(callback), check_achievements]() mutable {
720+
const u32 pending_unlock_count = check_achievements ? Achievements::GetPendingUnlockCount() : 0;
719721
const bool was_saving = System::IsSavingMemoryCards();
720-
GPUThread::RunOnThread([action = std::move(action), callback = std::move(callback), was_saving]() mutable {
721-
if (!was_saving)
722+
GPUThread::RunOnThread([action = std::move(action), callback = std::move(callback), pending_unlock_count,
723+
was_saving]() mutable {
724+
if (!was_saving && pending_unlock_count == 0)
722725
{
723726
callback(true);
724727
return;
725728
}
726729

727-
OpenConfirmMessageDialog(
728-
ICON_EMOJI_WARNING, FSUI_ICONVSTR(ICON_PF_MEMORY_CARD, "Memory Card Busy"),
729-
fmt::format(
730-
FSUI_FSTR("WARNING: Your game is still saving to the memory card. Continuing to {0} may IRREVERSIBLY "
731-
"DESTROY YOUR MEMORY CARD. We recommend resuming your game and waiting 5 seconds for it to "
732-
"finish saving.\n\nDo you want to {0} anyway?"),
733-
action),
734-
std::move(callback),
735-
fmt::format(
736-
fmt::runtime(FSUI_ICONVSTR(ICON_FA_TRIANGLE_EXCLAMATION, "Yes, {} now and risk memory card corruption.")),
737-
action),
738-
FSUI_ICONSTR(ICON_FA_PLAY, "No, resume the game."));
730+
if (was_saving)
731+
{
732+
OpenConfirmMessageDialog(
733+
ICON_EMOJI_WARNING, FSUI_ICONVSTR(ICON_PF_MEMORY_CARD, "Memory Card Busy"),
734+
fmt::format(
735+
FSUI_FSTR("WARNING: Your game is still saving to the memory card. Continuing to {0} may IRREVERSIBLY "
736+
"DESTROY YOUR MEMORY CARD. We recommend resuming your game and waiting 5 seconds for it to "
737+
"finish saving.\n\nDo you want to {0} anyway?"),
738+
action),
739+
std::move(callback),
740+
fmt::format(
741+
fmt::runtime(FSUI_ICONVSTR(ICON_FA_TRIANGLE_EXCLAMATION, "Yes, {} now and risk memory card corruption.")),
742+
action),
743+
FSUI_ICONSTR(ICON_FA_PLAY, "No, resume the game."));
744+
}
745+
else
746+
{
747+
OpenConfirmMessageDialog(
748+
ICON_EMOJI_WARNING, FSUI_ICONVSTR(ICON_FA_TROPHY, "Achievement Unlocks Unconfirmed"),
749+
fmt::format(FSUI_FSTR("{0} achievement unlocks have not been confirmed by the server. Continuing to {1} will "
750+
"result in loss of these unlocks. Once network connectivity has been re-established, "
751+
"these unlocks will be confirmed automatically.\n\nDo you want to {1} anyway?"),
752+
pending_unlock_count, action),
753+
std::move(callback));
754+
}
739755
});
740756
});
741757
}
@@ -744,7 +760,7 @@ void FullscreenUI::RequestShutdown(bool save_state)
744760
{
745761
SwitchToMainWindow(MainWindowType::None);
746762

747-
ConfirmIfSavingMemoryCards(FSUI_STR("shut down"), [save_state](bool result) {
763+
ConfirmWithSafetyCheck(FSUI_STR("shut down"), true, [save_state](bool result) {
748764
if (result)
749765
Host::RunOnCoreThread([save_state]() { Host::RequestSystemShutdown(false, save_state, false); });
750766
else
@@ -756,7 +772,7 @@ void FullscreenUI::RequestRestart()
756772
{
757773
SwitchToMainWindow(MainWindowType::None);
758774

759-
ConfirmIfSavingMemoryCards(FSUI_STR("restart"), [](bool result) {
775+
ConfirmWithSafetyCheck(FSUI_STR("restart"), false, [](bool result) {
760776
if (result)
761777
Host::RunOnCoreThread(System::ResetSystem);
762778

@@ -783,7 +799,7 @@ void FullscreenUI::StartChangeDiscFromFile()
783799
return;
784800
}
785801

786-
ConfirmIfSavingMemoryCards(FSUI_STR("change disc"), [path](bool result) {
802+
ConfirmWithSafetyCheck(FSUI_STR("change disc"), false, [path](bool result) {
787803
if (result)
788804
{
789805
if (!GameList::IsScannableFilename(path))
@@ -839,7 +855,7 @@ void FullscreenUI::BeginChangeDiscOnCoreThread(bool needs_pause)
839855
}
840856
else if (index > 0)
841857
{
842-
ConfirmIfSavingMemoryCards(FSUI_STR("change disc"), [index](bool result) {
858+
ConfirmWithSafetyCheck(FSUI_STR("change disc"), false, [index](bool result) {
843859
if (result)
844860
System::SwitchMediaSubImage(static_cast<u32>(index - 1));
845861

@@ -890,7 +906,7 @@ void FullscreenUI::BeginChangeDiscOnCoreThread(bool needs_pause)
890906
}
891907
else if (index > 0)
892908
{
893-
ConfirmIfSavingMemoryCards(FSUI_STR("change disc"), [paths = std::move(paths), index](bool result) {
909+
ConfirmWithSafetyCheck(FSUI_STR("change disc"), false, [paths = std::move(paths), index](bool result) {
894910
if (result)
895911
Host::RunOnCoreThread([path = std::move(paths[index - 1])]() { System::InsertMedia(path.c_str()); });
896912

src/duckstation-qt/qthost.cpp

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,46 +1079,70 @@ void CoreThread::reloadInputBindings()
10791079
System::ReloadInputBindings();
10801080
}
10811081

1082-
void CoreThread::confirmActionIfMemoryCardBusy(const QString& action, bool cancel_resume_on_accept,
1083-
std::function<void(bool)> callback) const
1082+
void CoreThread::confirmActionWithSafetyCheck(const QString& action, bool check_achievements,
1083+
bool cancel_resume_on_accept, std::function<void(bool)> callback) const
10841084
{
10851085
DebugAssert(isCurrentThread());
1086+
if (!System::IsValid())
1087+
{
1088+
callback(true);
1089+
return;
1090+
}
10861091

1087-
if (!System::IsValid() || !System::IsSavingMemoryCards())
1092+
const bool saving = System::IsSavingMemoryCards();
1093+
const u32 pending_unlock_count = Achievements::GetPendingUnlockCount();
1094+
if (!saving && pending_unlock_count == 0)
10881095
{
10891096
callback(true);
10901097
return;
10911098
}
10921099

1093-
Host::RunOnUIThread([action, cancel_resume_on_accept, callback = std::move(callback)]() mutable {
1094-
auto lock = g_main_window->pauseAndLockSystem();
1100+
Host::RunOnUIThread(
1101+
[callback = std::move(callback), action, saving, pending_unlock_count, cancel_resume_on_accept]() mutable {
1102+
auto lock = g_main_window->pauseAndLockSystem();
10951103

1096-
const bool result = (QtUtils::MessageBoxQuestion(
1097-
lock.getDialogParent(), tr("Memory Card Busy"),
1098-
tr("WARNING: Your game is still saving to the memory card. Continuing to %1 may "
1099-
"IRREVERSIBLY DESTROY YOUR MEMORY CARD. We recommend resuming your game and waiting 5 "
1100-
"seconds for it to finish saving.\n\nDo you want to %1 anyway?")
1101-
.arg(action)) != QMessageBox::No);
1104+
bool result;
1105+
if (saving)
1106+
{
1107+
result = (QtUtils::MessageBoxIcon(
1108+
lock.getDialogParent(), QMessageBox::Warning, tr("Memory Card Busy"),
1109+
tr("WARNING: Your game is still saving to the memory card. Continuing to %1 may "
1110+
"IRREVERSIBLY DESTROY YOUR MEMORY CARD. We recommend resuming your game and waiting 5 "
1111+
"seconds for it to finish saving.\n\nDo you want to %1 anyway?")
1112+
.arg(action),
1113+
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::No);
1114+
}
1115+
else
1116+
{
1117+
result = (QtUtils::MessageBoxIcon(
1118+
lock.getDialogParent(), QMessageBox::Warning, tr("Achievement Unlocks Unconfirmed"),
1119+
tr("%1 achievement unlocks have not been confirmed by the server. Continuing to %2 will result in "
1120+
"loss of these unlocks. Once network connectivity has been re-established, these unlocks will "
1121+
"be confirmed automatically.\n\nDo you want to %2 anyway?")
1122+
.arg(pending_unlock_count)
1123+
.arg(action),
1124+
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::No);
1125+
}
11021126

1103-
if (cancel_resume_on_accept && !QtHost::IsFullscreenUIStarted())
1104-
lock.cancelResume();
1127+
if (cancel_resume_on_accept && !QtHost::IsFullscreenUIStarted())
1128+
lock.cancelResume();
11051129

1106-
Host::RunOnCoreThread([result, callback = std::move(callback)]() { callback(result); });
1107-
});
1130+
Host::RunOnCoreThread([result, callback = std::move(callback)]() { callback(result); });
1131+
});
11081132
}
11091133

1110-
void CoreThread::shutdownSystem(bool save_state, bool check_memcard_busy)
1134+
void CoreThread::shutdownSystem(bool save_state, bool check_safety)
11111135
{
11121136
if (!isCurrentThread())
11131137
{
11141138
System::CancelPendingStartup();
1115-
QMetaObject::invokeMethod(this, &CoreThread::shutdownSystem, Qt::QueuedConnection, save_state, check_memcard_busy);
1139+
QMetaObject::invokeMethod(this, &CoreThread::shutdownSystem, Qt::QueuedConnection, save_state, check_safety);
11161140
return;
11171141
}
11181142

1119-
if (check_memcard_busy && System::IsSavingMemoryCards())
1143+
if (check_safety && (System::IsSavingMemoryCards() || Achievements::GetPendingUnlockCount() > 0))
11201144
{
1121-
confirmActionIfMemoryCardBusy(tr("shut down"), true, [save_state](bool result) {
1145+
confirmActionWithSafetyCheck(tr("shut down"), true, true, [save_state](bool result) {
11221146
if (result)
11231147
g_core_thread->shutdownSystem(save_state, false);
11241148
else
@@ -1140,7 +1164,7 @@ void CoreThread::resetSystem(bool check_memcard_busy)
11401164

11411165
if (check_memcard_busy && System::IsSavingMemoryCards())
11421166
{
1143-
confirmActionIfMemoryCardBusy(tr("reset"), false, [](bool result) {
1167+
confirmActionWithSafetyCheck(tr("reset"), false, false, [](bool result) {
11441168
if (result)
11451169
g_core_thread->resetSystem(false);
11461170
});
@@ -1172,7 +1196,7 @@ void CoreThread::changeDisc(const QString& new_disc_filename, bool reset_system,
11721196

11731197
if (check_memcard_busy && System::IsSavingMemoryCards())
11741198
{
1175-
confirmActionIfMemoryCardBusy(tr("change disc"), false, [new_disc_filename, reset_system](bool result) {
1199+
confirmActionWithSafetyCheck(tr("change disc"), false, false, [new_disc_filename, reset_system](bool result) {
11761200
if (result)
11771201
g_core_thread->changeDisc(new_disc_filename, reset_system, false);
11781202
});

src/duckstation-qt/qthost.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ class CoreThread : public QThread
149149
void exitFullscreenUI();
150150
void bootSystem(std::shared_ptr<SystemBootParameters> params);
151151
void resumeSystemFromMostRecentState();
152-
void shutdownSystem(bool save_state, bool check_memcard_busy);
152+
void shutdownSystem(bool save_state, bool check_safety);
153153
void resetSystem(bool check_memcard_busy);
154154
void setSystemPaused(bool paused);
155155
void changeDisc(const QString& new_disc_path, bool reset_system, bool check_memcard_busy);
@@ -203,8 +203,8 @@ class CoreThread : public QThread
203203

204204
void createBackgroundControllerPollTimer();
205205
void destroyBackgroundControllerPollTimer();
206-
void confirmActionIfMemoryCardBusy(const QString& action, bool cancel_resume_on_accept,
207-
std::function<void(bool)> callback) const;
206+
void confirmActionWithSafetyCheck(const QString& action, bool check_achievements, bool cancel_resume_on_accept,
207+
std::function<void(bool)> callback) const;
208208

209209
static void gpuThreadEntryPoint();
210210

0 commit comments

Comments
 (0)