From 05f0c1b0706114a37c78c308f6bd4d91be5875f0 Mon Sep 17 00:00:00 2001 From: Nick Siakas Date: Wed, 14 Oct 2020 18:50:26 +0300 Subject: [PATCH] Set Game metadata --- xbmc/Application.cpp | 13 +++++ xbmc/GUIInfoManager.cpp | 9 ++++ xbmc/GUIInfoManager.h | 11 +++++ xbmc/cores/RetroPlayer/RetroPlayer.cpp | 55 ++++++++++++++++++---- xbmc/cores/RetroPlayer/RetroPlayer.h | 13 ++++- xbmc/cores/RetroPlayer/cheevos/Cheevos.cpp | 34 +++++++++---- xbmc/cores/RetroPlayer/cheevos/Cheevos.h | 36 ++++++-------- xbmc/interfaces/legacy/Player.cpp | 6 ++- xbmc/messaging/ApplicationMessenger.h | 2 + 9 files changed, 138 insertions(+), 41 deletions(-) diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index ad22f178901db..faf83aef0a237 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -2380,6 +2380,19 @@ void CApplication::OnApplicationMessage(ThreadMessage* pMsg) } break; + case TMSG_SET_PLAYER_ITEM: + { + CFileItem* item = static_cast(pMsg->lpVoid); + if (item == nullptr) + return; + + *m_itemCurrentFile = *item; + CServiceBroker::GetGUI()->GetInfoManager().SetCurrentItem(*m_itemCurrentFile); + + delete item; + } + break; + default: CLog::Log(LOGERROR, "%s: Unhandled threadmessage sent, %u", __FUNCTION__, msg); break; diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index c846e3ab4c38f..8c91f69e9059c 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -15,6 +15,7 @@ #include "Util.h" #include "cores/DataCacheCore.h" #include "filesystem/File.h" +#include "games/tags/GameInfoTag.h" #include "guilib/guiinfo/GUIInfo.h" #include "guilib/guiinfo/GUIInfoHelper.h" #include "guilib/guiinfo/GUIInfoLabels.h" @@ -10964,6 +10965,14 @@ const CVideoInfoTag* CGUIInfoManager::GetCurrentMovieTag() const return nullptr; } +const KODI::GAME::CGameInfoTag* CGUIInfoManager::GetCurrentGameTag() const +{ + if (m_currentFile->HasGameInfoTag()) + return m_currentFile->GetGameInfoTag(); + + return nullptr; +} + int CGUIInfoManager::RegisterSkinVariableString(const CSkinVariableString* info) { if (!info) diff --git a/xbmc/GUIInfoManager.h b/xbmc/GUIInfoManager.h index 9206dc0aee6ad..730b9e414225e 100644 --- a/xbmc/GUIInfoManager.h +++ b/xbmc/GUIInfoManager.h @@ -28,6 +28,10 @@ typedef std::shared_ptr CGUIListItemPtr; namespace KODI { +namespace GAME +{ + class CGameInfoTag; +} namespace GUILIB { namespace GUIINFO @@ -109,6 +113,10 @@ class CGUIInfoManager : public KODI::MESSAGING::IMessageTarget bool GetItemInt(int &value, const CGUIListItem *item, int contextWindow, int info) const; bool GetItemBool(const CGUIListItem *item, int contextWindow, int condition) const; + /*! \brief Get currently playing file item + */ + const CFileItem& GetCurrentItem() const { return *m_currentFile; } + /*! \brief Set currently playing file item */ void SetCurrentItem(const CFileItem &item); @@ -122,6 +130,9 @@ class CGUIInfoManager : public KODI::MESSAGING::IMessageTarget // Current video stuff const CVideoInfoTag* GetCurrentMovieTag() const; + // Current game stuff + const KODI::GAME::CGameInfoTag* GetCurrentGameTag() const; + void UpdateAVInfo(); int RegisterSkinVariableString(const INFO::CSkinVariableString* info); diff --git a/xbmc/cores/RetroPlayer/RetroPlayer.cpp b/xbmc/cores/RetroPlayer/RetroPlayer.cpp index ab35e18e6d37b..a3982ce421cba 100644 --- a/xbmc/cores/RetroPlayer/RetroPlayer.cpp +++ b/xbmc/cores/RetroPlayer/RetroPlayer.cpp @@ -9,6 +9,7 @@ #include "RetroPlayer.h" #include "FileItem.h" +#include "GUIInfoManager.h" #include "RetroPlayerAutoSave.h" #include "RetroPlayerInput.h" #include "ServiceBroker.h" @@ -40,6 +41,7 @@ #include "guilib/WindowIDs.h" #include "input/actions/Action.h" #include "input/actions/ActionIDs.h" +#include "interfaces/AnnouncementManager.h" #include "messaging/ApplicationMessenger.h" #include "settings/MediaSettings.h" #include "threads/SingleLock.h" @@ -54,21 +56,25 @@ using namespace GAME; using namespace RETRO; CRetroPlayer::CRetroPlayer(IPlayerCallback& callback) - : IPlayer(callback), m_gameServices(CServiceBroker::GetGameServices()) + : IPlayer(callback), + m_gameServices(CServiceBroker::GetGameServices()), + m_fileItem(new CFileItem()) { ResetPlayback(); CServiceBroker::GetWinSystem()->RegisterRenderLoop(this); + CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this); } CRetroPlayer::~CRetroPlayer() { CServiceBroker::GetWinSystem()->UnregisterRenderLoop(this); + CServiceBroker::GetAnnouncementManager()->RemoveAnnouncer(this); CloseFile(); } bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options) { - CFileItem fileCopy(file); + *m_fileItem = file; std::string savestatePath; @@ -76,7 +82,7 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options // This will prompt the user to select a savestate if there are any. // If there are no savestates, or the user wants to create a new savestate // it will prompt the user to select a game client - if (!GAME::CGameUtils::FillInGameClient(fileCopy, savestatePath)) + if (!GAME::CGameUtils::FillInGameClient(*m_fileItem, savestatePath)) { CLog::Log(LOGINFO, "RetroPlayer[PLAYER]: No compatible game client selected, aborting playback"); @@ -84,7 +90,7 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options } // Check if we should open in standalone mode - const bool bStandalone = fileCopy.GetPath().empty(); + const bool bStandalone = m_fileItem->GetPath().empty(); m_processInfo.reset(CRPProcessInfo::CreateInstance()); if (!m_processInfo) @@ -103,11 +109,11 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options if (IsPlaying()) CloseFile(); - PrintGameInfo(fileCopy); + PrintGameInfo(*m_fileItem); bool bSuccess = false; - std::string gameClientId = fileCopy.GetGameInfoTag()->GetGameClient(); + std::string gameClientId = m_fileItem->GetGameInfoTag()->GetGameClient(); ADDON::AddonPtr addon; if (gameClientId.empty()) @@ -131,9 +137,9 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options if (!bStandalone) { - std::string redactedPath = CURL::GetRedacted(fileCopy.GetPath()); + std::string redactedPath = CURL::GetRedacted(m_fileItem->GetPath()); CLog::Log(LOGINFO, "RetroPlayer[PLAYER]: Opening: %s", redactedPath.c_str()); - bSuccess = m_gameClient->OpenFile(fileCopy, *m_streamManager, m_input.get()); + bSuccess = m_gameClient->OpenFile(*m_fileItem, *m_streamManager, m_input.get()); } else { @@ -182,11 +188,15 @@ bool CRetroPlayer::OpenFile(const CFileItem& file, const CPlayerOptions& options // Switch to fullscreen MESSAGING::CApplicationMessenger::GetInstance().PostMsg(TMSG_SWITCHTOFULLSCREEN); - m_cheevos.reset(new CCheevos(m_gameClient.get(), m_gameServices.GameSettings().RAUsername(), + m_cheevos.reset(new CCheevos(m_gameClient.get(), *m_fileItem, + m_gameServices.GameSettings().RAUsername(), m_gameServices.GameSettings().RAToken())); m_cheevos->EnableRichPresence(); + // Calls to external code could mutate file item, so make a copy + CFileItem fileCopy(*m_fileItem); + // Initialize gameplay CreatePlayback(m_gameServices.GameSettings().AutosaveEnabled(), savestatePath); RegisterWindowCallbacks(); @@ -570,6 +580,33 @@ std::string CRetroPlayer::CreateAutosave() return m_playback->CreateSavestate(true); } +void CRetroPlayer::Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) +{ + // Announce() is called at the end of a chain to update the currently-playing + // file. + // + // Updates to current file metadata should find their way to CApplication. + // This can be accomplished off-thread by sending a TMSG_SET_PLAYER_ITEM + // message via app messenger. + // + // When CApplication receives a file metadata update, it in turn updates + // the state of CGUIInfoManager. + // + // CGUIInfoManager fires an "OnChanged" event for the info update. Publishers + // of file item metadata should be subscribed to this event to receive info + // updates from other publishers, as well as info added by CApplication + // during the playback lifecycle. + + if (flag == ANNOUNCEMENT::Info && message == "OnChanged") + { + const CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager(); + *m_fileItem = infoMgr.GetCurrentItem(); + } +} + void CRetroPlayer::SetSpeedInternal(double speed) { OnSpeedChange(speed); diff --git a/xbmc/cores/RetroPlayer/RetroPlayer.h b/xbmc/cores/RetroPlayer/RetroPlayer.h index 7c2581754b033..494f121d39674 100644 --- a/xbmc/cores/RetroPlayer/RetroPlayer.h +++ b/xbmc/cores/RetroPlayer/RetroPlayer.h @@ -14,6 +14,7 @@ #include "cores/RetroPlayer/playback/IPlaybackControl.h" #include "games/GameTypes.h" #include "guilib/DispResource.h" +#include "interfaces/IAnnouncer.h" #include "threads/CriticalSection.h" #include @@ -38,7 +39,8 @@ class CRetroPlayer : public IPlayer, public IRenderLoop, public IGameCallback, public IPlaybackCallback, - public IAutoSaveCallback + public IAutoSaveCallback, + public ANNOUNCEMENT::IAnnouncer { public: explicit CRetroPlayer(IPlayerCallback& callback); @@ -84,6 +86,12 @@ class CRetroPlayer : public IPlayer, bool IsAutoSaveEnabled() const override; std::string CreateAutosave() override; + // Implementation of IAnnouncer + void Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) override; + private: void SetSpeedInternal(double speed); @@ -136,6 +144,9 @@ class CRetroPlayer : public IPlayer, // Synchronization parameters CCriticalSection m_mutex; + + // File metadata + std::shared_ptr m_fileItem; }; } // namespace RETRO } // namespace KODI diff --git a/xbmc/cores/RetroPlayer/cheevos/Cheevos.cpp b/xbmc/cores/RetroPlayer/cheevos/Cheevos.cpp index 8666b468f5669..e333c7a934375 100644 --- a/xbmc/cores/RetroPlayer/cheevos/Cheevos.cpp +++ b/xbmc/cores/RetroPlayer/cheevos/Cheevos.cpp @@ -8,10 +8,13 @@ #include "Cheevos.h" +#include "FileItem.h" #include "URL.h" #include "filesystem/CurlFile.h" #include "filesystem/File.h" #include "games/addons/GameClient.h" +#include "games/tags/GameInfoTag.h" +#include "messaging/ApplicationMessenger.h" #include "utils/JSONVariantParser.h" #include "utils/URIUtils.h" #include "utils/Variant.h" @@ -39,9 +42,13 @@ constexpr int URL_SIZE = 512; constexpr int POST_DATA_SIZE = 1024; CCheevos::CCheevos(GAME::CGameClient* gameClient, - const std::string userName, - const std::string loginToken) - : m_gameClient(gameClient), m_userName(userName), m_loginToken(loginToken) + CFileItem& fileItem, + std::string userName, + std::string loginToken) + : m_gameClient(gameClient), + m_fileItem(fileItem), + m_userName(std::move(userName)), + m_loginToken(std::move(loginToken)) { } @@ -116,12 +123,13 @@ bool CCheevos::LoadData() m_richPresenceScript = data[PATCH_DATA][RICH_PRESENCE].asString(); m_richPresenceLoaded = true; - m_title = data[PATCH_DATA][GAME_TITLE].asString(); - m_publisher = data[PATCH_DATA][PUBLISHER].asString(); - m_developer = data[PATCH_DATA][DEVELOPER].asString(); - m_genre = data[PATCH_DATA][GENRE].asString(); - m_consoleName = data[PATCH_DATA][CONSOLE_NAME].asString(); - m_released = data[PATCH_DATA][RELEASED].asString(); + GAME::CGameInfoTag& tag = *m_fileItem.GetGameInfoTag(); + + tag.SetTitle(data[PATCH_DATA][GAME_TITLE].asString()); + tag.SetPublisher(data[PATCH_DATA][PUBLISHER].asString()); + tag.SetDeveloper(data[PATCH_DATA][DEVELOPER].asString()); + tag.SetGenres({data[PATCH_DATA][GENRE].asString()}); + tag.SetPlatform(data[PATCH_DATA][CONSOLE_NAME].asString()); return true; } @@ -151,6 +159,14 @@ bool CCheevos::GetRichPresenceEvaluation(char* evaluation, size_t size) m_gameClient->GetRichPresenceEvaluation(evaluation, size); + GAME::CGameInfoTag& tag = *m_fileItem.GetGameInfoTag(); + + tag.SetCaption(evaluation); + + CFileItem* file = new CFileItem(m_fileItem); + MESSAGING::CApplicationMessenger::GetInstance().PostMsg(TMSG_SET_PLAYER_ITEM, -1, -1, + static_cast(file)); + char url[URL_SIZE]; char postData[POST_DATA_SIZE]; if (m_gameClient->RCPostRichPresenceUrl(url, URL_SIZE, postData, POST_DATA_SIZE, diff --git a/xbmc/cores/RetroPlayer/cheevos/Cheevos.h b/xbmc/cores/RetroPlayer/cheevos/Cheevos.h index e8f8337102392..e8d3d4ea1937b 100644 --- a/xbmc/cores/RetroPlayer/cheevos/Cheevos.h +++ b/xbmc/cores/RetroPlayer/cheevos/Cheevos.h @@ -13,6 +13,8 @@ #include #include +class CFileItem; + namespace KODI { namespace GAME @@ -25,36 +27,28 @@ namespace RETRO class CCheevos { public: - CCheevos(GAME::CGameClient* gameClient, const std::string userName, const std::string loginToken); + CCheevos(GAME::CGameClient* gameClient, + CFileItem& fileItem, + std::string userName, + std::string loginToken); void ResetRuntime(); void EnableRichPresence(); bool GetRichPresenceEvaluation(char* evaluation, size_t size); - std::string GetTitle() { return m_title; } - std::string GetPublisher() { return m_publisher; } - std::string GetDeveloper() { return m_developer; } - std::string GetGenre() { return m_genre; } - std::string GetConsoleName() { return m_consoleName; } - std::string GetReleased() { return m_released; } - private: bool LoadData(); + // Construction parameters GAME::CGameClient* const m_gameClient; - std::string m_userName; - std::string m_loginToken; - std::string m_romHash{}; - std::string m_richPresenceScript{}; - unsigned m_gameID{}; - bool m_richPresenceLoaded{}; + CFileItem& m_fileItem; + const std::string m_userName; + const std::string m_loginToken; - // Game metadata - std::string m_title; - std::string m_publisher; - std::string m_developer; - std::string m_genre; - std::string m_consoleName; - std::string m_released; + // Game parameters + std::string m_romHash; + std::string m_richPresenceScript; + unsigned m_gameID = 0; + bool m_richPresenceLoaded = false; const std::map m_extensionToConsole = {{".a26", RC_CONSOLE_ATARI_2600}, {".a78", RC_CONSOLE_ATARI_7800}, diff --git a/xbmc/interfaces/legacy/Player.cpp b/xbmc/interfaces/legacy/Player.cpp index 7bdde745e7ca6..311acf1e786a4 100644 --- a/xbmc/interfaces/legacy/Player.cpp +++ b/xbmc/interfaces/legacy/Player.cpp @@ -363,7 +363,11 @@ namespace XBMCAddon if (!g_application.GetAppPlayer().IsPlayingGame()) throw PlayerException("XBMC is not playing any game file"); - return new InfoTagGame(*g_application.CurrentFileItem().GetGameInfoTag()); + const KODI::GAME::CGameInfoTag* game = CServiceBroker::GetGUI()->GetInfoManager().GetCurrentGameTag(); + if (game != nullptr) + return new InfoTagGame(*game); + + return new InfoTagGame(); } InfoTagVideo* Player::getVideoInfoTag() diff --git a/xbmc/messaging/ApplicationMessenger.h b/xbmc/messaging/ApplicationMessenger.h index a1c6b60d521fd..670879eeaa3c3 100644 --- a/xbmc/messaging/ApplicationMessenger.h +++ b/xbmc/messaging/ApplicationMessenger.h @@ -82,6 +82,8 @@ #define TMSG_RENDERER_PREINIT TMSG_MASK_APPLICATION + 31 #define TMSG_RENDERER_UNINIT TMSG_MASK_APPLICATION + 32 #define TMSG_EVENT TMSG_MASK_APPLICATION + 33 +/// @brief Called from the player when its current item is updated +#define TMSG_SET_PLAYER_ITEM TMSG_MASK_APPLICATION + 34 #define TMSG_GUI_INFOLABEL TMSG_MASK_GUIINFOMANAGER + 0 #define TMSG_GUI_INFOBOOL TMSG_MASK_GUIINFOMANAGER + 1