From 563dc9c756bf0b0d7ae05224d2c093c7cfa70efa Mon Sep 17 00:00:00 2001 From: Dentomologist Date: Sun, 16 Mar 2025 14:41:18 -0700 Subject: [PATCH] GameList: Update time played while game is running Add an UpdateEvent for time played updates and use it to update the Game List time played while a game is running (instead of just when it stops). This removes one of the two calls to `Reload`, and we can move its code to the other caller in TimePlayedManager's constructor and avoid locking the mutex unnecessarily. --- Source/Core/Core/TimePlayed.cpp | 26 +++++++------ Source/Core/Core/TimePlayed.h | 3 +- .../Core/DolphinQt/GameList/GameListModel.cpp | 37 +++++++++++++------ .../Core/DolphinQt/GameList/GameListModel.h | 4 +- 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/Source/Core/Core/TimePlayed.cpp b/Source/Core/Core/TimePlayed.cpp index 00e72f4e70..b686292a41 100644 --- a/Source/Core/Core/TimePlayed.cpp +++ b/Source/Core/Core/TimePlayed.cpp @@ -9,13 +9,15 @@ #include "Common/CommonTypes.h" #include "Common/FileUtil.h" +#include "Common/HookableEvent.h" #include "Common/IniFile.h" #include "Common/NandPaths.h" TimePlayedManager::TimePlayedManager() : m_ini_path(File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini") { - Reload(); + m_ini.Load(m_ini_path); + m_time_list = m_ini.GetOrCreateSection("TimePlayed"); } TimePlayedManager::~TimePlayedManager() = default; @@ -30,11 +32,18 @@ void TimePlayedManager::AddTime(const std::string& game_id, std::chrono::millise { std::string filtered_game_id = Common::EscapeFileName(game_id); u64 previous_time; + u64 new_time; - std::lock_guard guard(m_mutex); - m_time_list->Get(filtered_game_id, &previous_time); - m_time_list->Set(filtered_game_id, previous_time + static_cast(time_emulated.count())); - m_ini.Save(m_ini_path); + { + std::lock_guard guard(m_mutex); + + m_time_list->Get(filtered_game_id, &previous_time); + new_time = previous_time + static_cast(time_emulated.count()); + m_time_list->Set(filtered_game_id, new_time); + m_ini.Save(m_ini_path); + } + + update_event.Trigger(filtered_game_id, static_cast(new_time)); } std::chrono::milliseconds TimePlayedManager::GetTimePlayed(const std::string& game_id) const @@ -46,10 +55,3 @@ std::chrono::milliseconds TimePlayedManager::GetTimePlayed(const std::string& ga m_time_list->Get(filtered_game_id, &previous_time); return std::chrono::milliseconds(previous_time); } - -void TimePlayedManager::Reload() -{ - std::lock_guard guard(m_mutex); - m_ini.Load(m_ini_path); - m_time_list = m_ini.GetOrCreateSection("TimePlayed"); -} diff --git a/Source/Core/Core/TimePlayed.h b/Source/Core/Core/TimePlayed.h index 6adfc56348..91a9656e4c 100644 --- a/Source/Core/Core/TimePlayed.h +++ b/Source/Core/Core/TimePlayed.h @@ -8,6 +8,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/HookableEvent.h" #include "Common/IniFile.h" class TimePlayedManager @@ -26,7 +27,7 @@ public: std::chrono::milliseconds GetTimePlayed(const std::string& game_id) const; - void Reload(); + Common::HookableEvent update_event; private: TimePlayedManager(); diff --git a/Source/Core/DolphinQt/GameList/GameListModel.cpp b/Source/Core/DolphinQt/GameList/GameListModel.cpp index 93edcef63f..b5a892c3e9 100644 --- a/Source/Core/DolphinQt/GameList/GameListModel.cpp +++ b/Source/Core/DolphinQt/GameList/GameListModel.cpp @@ -3,18 +3,21 @@ #include "DolphinQt/GameList/GameListModel.h" +#include +#include + #include #include #include #include #include "Core/Config/MainSettings.h" -#include "Core/Core.h" #include "Core/TimePlayed.h" #include "DiscIO/Enums.h" #include "DolphinQt/QtUtils/ImageConverter.h" +#include "DolphinQt/QtUtils/QueueOnObject.h" #include "DolphinQt/Resources.h" #include "DolphinQt/Settings.h" @@ -35,8 +38,6 @@ GameListModel::GameListModel(QObject* parent) &GameTracker::RefreshAll); connect(&Settings::Instance(), &Settings::TitleDBReloadRequested, [this] { m_title_database = Core::TitleDatabase(); }); - connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, - &GameListModel::OnEmulationStateChanged); for (const QString& dir : Settings::Instance().GetPaths()) m_tracker.AddDirectory(dir); @@ -50,6 +51,28 @@ GameListModel::GameListModel(QObject* parent) emit layoutChanged(); }); + const auto on_time_played_update = [this](const std::string& game_id, + const std::chrono::milliseconds) { + const auto update_cell = [this, game_id]() { + for (int model_row = 0; model_row < m_games.size(); ++model_row) + { + if (game_id != m_games[model_row]->GetGameID()) + continue; + + const QModelIndex time_played_index = + index(model_row, static_cast(Column::TimePlayed)); + emit dataChanged(time_played_index, time_played_index); + + // Multiple entries in the GameList can have the same GameID, so don't break out of the + // loop when a match is found. + } + }; + QueueOnObject(this, update_cell); + }; + + m_time_played_update_event = + TimePlayedManager::GetInstance().update_event.Register(on_time_played_update); + auto& settings = Settings::GetQSettings(); m_tag_list = settings.value(QStringLiteral("gamelist/tags")).toStringList(); @@ -522,11 +545,3 @@ void GameListModel::PurgeCache() { m_tracker.PurgeCache(); } - -void GameListModel::OnEmulationStateChanged(Core::State state) -{ - if (state == Core::State::Uninitialized) - { - m_time_played_manager.Reload(); - } -} diff --git a/Source/Core/DolphinQt/GameList/GameListModel.h b/Source/Core/DolphinQt/GameList/GameListModel.h index f513a1dde1..f4213a0708 100644 --- a/Source/Core/DolphinQt/GameList/GameListModel.h +++ b/Source/Core/DolphinQt/GameList/GameListModel.h @@ -12,7 +12,6 @@ #include #include -#include "Core/Core.h" #include "Core/TimePlayed.h" #include "Core/TitleDatabase.h" @@ -90,8 +89,6 @@ private: // Index in m_games, or -1 if it isn't found int FindGameIndex(const std::string& path) const; - void OnEmulationStateChanged(Core::State state); - QStringList m_tag_list; QMap m_game_tags; @@ -99,6 +96,7 @@ private: QList> m_games; Core::TitleDatabase m_title_database; TimePlayedManager& m_time_played_manager; + Common::EventHook m_time_played_update_event; QString m_term; float m_scale = 1.0; };