Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track Time Played (Core and QT) #11469

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Source/Core/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,8 @@ add_library(core
SysConf.h
System.cpp
System.h
TimePlayed.cpp
TimePlayed.h
TitleDatabase.cpp
TitleDatabase.h
WC24PatchEngine.cpp
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/Core/Config/MainSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ const Info<bool> MAIN_RENDER_WINDOW_AUTOSIZE{{System::Main, "Display", "RenderWi
false};
const Info<bool> MAIN_KEEP_WINDOW_ON_TOP{{System::Main, "Display", "KeepWindowOnTop"}, false};
const Info<bool> MAIN_DISABLE_SCREENSAVER{{System::Main, "Display", "DisableScreenSaver"}, true};
const Info<bool> MAIN_TIME_TRACKING{{System::Main, "Display", "TimeTracking"}, true};

// Main.DSP

Expand Down Expand Up @@ -459,6 +460,8 @@ const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE{{System::Main, "GameList", "Col
false};
const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION{{System::Main, "GameList", "ColumnCompression"},
false};
const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED{{System::Main, "GameList", "ColumnTimePlayed"},
true};
const Info<bool> MAIN_GAMELIST_COLUMN_TAGS{{System::Main, "GameList", "ColumnTags"}, false};

// Main.FifoPlayer
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/Config/MainSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ extern const Info<int> MAIN_RENDER_WINDOW_HEIGHT;
extern const Info<bool> MAIN_RENDER_WINDOW_AUTOSIZE;
extern const Info<bool> MAIN_KEEP_WINDOW_ON_TOP;
extern const Info<bool> MAIN_DISABLE_SCREENSAVER;
extern const Info<bool> MAIN_TIME_TRACKING;

// Main.General

Expand Down Expand Up @@ -295,6 +296,7 @@ extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_FILE_FORMAT;
extern const Info<bool> MAIN_GAMELIST_COLUMN_BLOCK_SIZE;
extern const Info<bool> MAIN_GAMELIST_COLUMN_COMPRESSION;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TIME_PLAYED;
extern const Info<bool> MAIN_GAMELIST_COLUMN_TAGS;

// Main.FifoPlayer
Expand Down
42 changes: 42 additions & 0 deletions Source/Core/Core/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <algorithm>
#include <climits>
#include <memory>
#include <mutex>
#include <optional>
#include <sstream>
#include <string>
Expand Down Expand Up @@ -98,14 +99,52 @@ void SConfig::LoadSettings()
Config::Load();
}

const std::string SConfig::GetGameID() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_game_id;
}

const std::string SConfig::GetGameTDBID() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_gametdb_id;
}

const std::string SConfig::GetTitleName() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_title_name;
}

const std::string SConfig::GetTitleDescription() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_title_description;
}

u64 SConfig::GetTitleID() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_title_id;
}

u16 SConfig::GetRevision() const
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
return m_revision;
}

void SConfig::ResetRunningGameMetadata()
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown);
}

void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
const DiscIO::Partition& partition)
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
if (partition == volume.GetGamePartition())
{
SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(),
Expand All @@ -122,6 +161,7 @@ void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,

void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform)
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
const u64 tmd_title_id = tmd.GetTitleId();

// If we're launching a disc game, we want to read the revision from
Expand All @@ -139,12 +179,14 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat

void SConfig::SetRunningGameMetadata(const std::string& game_id)
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown);
}

void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
u64 title_id, u16 revision, DiscIO::Region region)
{
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id ||
m_title_id != title_id || m_revision != revision;
m_game_id = game_id;
Expand Down
15 changes: 9 additions & 6 deletions Source/Core/Core/ConfigManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include <limits>
#include <mutex>
#include <optional>
#include <set>
#include <string>
Expand Down Expand Up @@ -58,15 +59,16 @@ struct SConfig
std::string m_strSRAM;

std::string m_debugger_game_id;

// TODO: remove this as soon as the ticket view hack in IOS/ES/Views is dropped.
bool m_disc_booted_from_game_list = false;

const std::string& GetGameID() const { return m_game_id; }
const std::string& GetGameTDBID() const { return m_gametdb_id; }
const std::string& GetTitleName() const { return m_title_name; }
const std::string& GetTitleDescription() const { return m_title_description; }
u64 GetTitleID() const { return m_title_id; }
u16 GetRevision() const { return m_revision; }
const std::string GetGameID() const;
const std::string GetGameTDBID() const;
const std::string GetTitleName() const;
const std::string GetTitleDescription() const;
u64 GetTitleID() const;
u16 GetRevision() const;
void ResetRunningGameMetadata();
void SetRunningGameMetadata(const DiscIO::Volume& volume, const DiscIO::Partition& partition);
void SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform);
Expand Down Expand Up @@ -114,6 +116,7 @@ struct SConfig
u64 title_id, u16 revision, DiscIO::Region region);

static SConfig* m_Instance;
mutable std::recursive_mutex m_metadata_lock;

std::string m_game_id;
std::string m_gametdb_id;
Expand Down
53 changes: 53 additions & 0 deletions Source/Core/Core/HW/CPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
#include "AudioCommon/AudioCommon.h"
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/Timer.h"
#include "Core/CPUThreadConfigCallback.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/Host.h"
#include "Core/PowerPC/GDBStub.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"
#include "Core/TimePlayed.h"
#include "VideoCommon/Fifo.h"

namespace CPU
Expand Down Expand Up @@ -63,6 +67,40 @@ void CPUManager::ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock)
}
}

void CPUManager::StartTimePlayedTimer()
{
// Steady clock for greater accuracy of timing
std::chrono::steady_clock timer;
auto prev_time = timer.now();

while (true)
{
const std::string game_id = SConfig::GetInstance().GetGameID();
aminoa marked this conversation as resolved.
Show resolved Hide resolved
TimePlayed time_played(game_id);
auto curr_time = timer.now();

// Check that emulation is not paused
// If the emulation is paused, wait for SetStepping() to reactivate
if (m_state == State::Running)
{
auto diff_time = std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - prev_time);
time_played.AddTime(diff_time);
}
else if (m_state == State::Stepping)
{
m_time_played_finish_sync.Wait();
curr_time = timer.now();
}

prev_time = curr_time;

if (m_state == State::PowerDown)
return;

m_time_played_finish_sync.WaitFor(std::chrono::seconds(30));
aminoa marked this conversation as resolved.
Show resolved Hide resolved
}
}

void CPUManager::Run()
{
auto& power_pc = m_system.GetPowerPC();
Expand All @@ -71,6 +109,13 @@ void CPUManager::Run()
// We can't rely on PowerPC::Init doing it, since it's called from EmuThread.
PowerPC::RoundingModeUpdated(power_pc.GetPPCState());

// Start a separate time tracker thread
std::thread timing;
if (Config::Get(Config::MAIN_TIME_TRACKING))
{
timing = std::thread(&CPUManager::StartTimePlayedTimer, this);
}

std::unique_lock state_lock(m_state_change_lock);
while (m_state != State::PowerDown)
{
Expand Down Expand Up @@ -165,6 +210,13 @@ void CPUManager::Run()
break;
}
}

if (timing.joinable())
{
m_time_played_finish_sync.Set();
timing.join();
}

state_lock.unlock();
Host_UpdateDisasmDialog();
}
Expand Down Expand Up @@ -266,6 +318,7 @@ void CPUManager::SetStepping(bool stepping)
else if (SetStateLocked(State::Running))
{
m_state_cpu_cvar.notify_one();
m_time_played_finish_sync.Set();
RunAdjacentSystems(true);
}
}
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/Core/HW/CPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <mutex>
#include <queue>

#include "Common/Event.h"

aminoa marked this conversation as resolved.
Show resolved Hide resolved
namespace Common
{
class Event;
Expand Down Expand Up @@ -102,6 +104,7 @@ class CPUManager
private:
void FlushStepSyncEventLocked();
void ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock);
void StartTimePlayedTimer();
void RunAdjacentSystems(bool running);
bool SetStateLocked(State s);

Expand Down Expand Up @@ -133,6 +136,7 @@ class CPUManager
bool m_state_cpu_step_instruction = false;
Common::Event* m_state_cpu_step_instruction_sync = nullptr;
std::queue<std::function<void()>> m_pending_jobs;
Common::Event m_time_played_finish_sync;

Core::System& m_system;
};
Expand Down
65 changes: 65 additions & 0 deletions Source/Core/Core/TimePlayed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#pragma once

#include <string>

#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Common/NandPaths.h"
#include "TimePlayed.h"

// used for QT interface - general access to time played for games
TimePlayed::TimePlayed()
{
m_game_id = "";
m_ini_path = File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini";
m_ini.Load(m_ini_path);
m_time_list = m_ini.GetOrCreateSection("Time Played");
}

TimePlayed::TimePlayed(std::string game_id)
{
// filter for unsafe characters
m_game_id = Common::EscapeFileName(game_id);
m_ini_path = File::GetUserPath(D_CONFIG_IDX) + "TimePlayed.ini";
m_ini.Load(m_ini_path);
m_time_list = m_ini.GetOrCreateSection("Time Played");
}

void TimePlayed::AddTime(std::chrono::milliseconds time_emulated)
{
if (m_game_id == "")
{
return;
}

u64 previous_time;
m_time_list->Get(m_game_id, &previous_time);
m_time_list->Set(m_game_id, previous_time + u64(time_emulated.count()));
m_ini.Save(m_ini_path);
}

std::chrono::milliseconds TimePlayed::GetTimePlayed() const
{
if (m_game_id == "")
{
return std::chrono::milliseconds(0);
}

u64 previous_time;
aminoa marked this conversation as resolved.
Show resolved Hide resolved
m_time_list->Get(m_game_id, &previous_time);
return std::chrono::milliseconds(previous_time);
}

std::chrono::milliseconds TimePlayed::GetTimePlayed(std::string game_id) const
{
std::string filtered_game_id = Common::EscapeFileName(game_id);
u64 previous_time;
m_time_list->Get(filtered_game_id, &previous_time);
return std::chrono::milliseconds(previous_time);
}

void TimePlayed::Reload()
{
m_ini.Load(m_ini_path);
m_time_list = m_ini.GetOrCreateSection("Time Played");
}
24 changes: 24 additions & 0 deletions Source/Core/Core/TimePlayed.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once
#include "Common/CommonTypes.h"
#include "Common/IniFile.h"

class TimePlayed
{
public:
TimePlayed();
TimePlayed(std::string game_id);

void AddTime(std::chrono::milliseconds time_emulated);
aminoa marked this conversation as resolved.
Show resolved Hide resolved

std::chrono::milliseconds GetTimePlayed() const;
std::chrono::milliseconds GetTimePlayed(std::string game_id) const;

void Reload();

private:
std::string m_game_id;
Common::IniFile m_ini;
std::string m_ini_path;

Common::IniFile::Section* m_time_list;
};
2 changes: 2 additions & 0 deletions Source/Core/DolphinLib.props
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@
<ClInclude Include="Core\SyncIdentifier.h" />
<ClInclude Include="Core\SysConf.h" />
<ClInclude Include="Core\System.h" />
<ClInclude Include="Core\TimePlayed.h" />
<ClInclude Include="Core\TitleDatabase.h" />
<ClInclude Include="Core\WC24PatchEngine.h" />
<ClInclude Include="Core\WiiRoot.h" />
Expand Down Expand Up @@ -1125,6 +1126,7 @@
<ClCompile Include="Core\State.cpp" />
<ClCompile Include="Core\SysConf.cpp" />
<ClCompile Include="Core\System.cpp" />
<ClCompile Include="Core\TimePlayed.cpp" />
<ClCompile Include="Core\TitleDatabase.cpp" />
<ClCompile Include="Core\WiiRoot.cpp" />
<ClCompile Include="Core\WiiUtils.cpp" />
Expand Down
Loading