added discord rpc (#1178)

* added discord rpc

* linting issues

* Revert "linting issues"

This reverts commit 55f4e39506.

* fix clang-format issues

* removed wrong rpc submodule

* added correct rpc submodule

* Moved cmake instructions to correct files.

* added minor suggestions from pr

* Added an option to enable/disable RPC, added rpc to emulator.cpp making it work on nonqt builds

* typo & minor stuff

* Changed getInstance implementation with Singleton class.

* Update discord_rpc_handler.cpp

discord id

* fixed ci clangformat errors

---------

Co-authored-by: georgemoralis <giorgosmrls@gmail.com>
This commit is contained in:
delledev 2024-10-08 18:14:37 +03:00 committed by GitHub
parent aba803bd04
commit 3e7137cc6b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 133 additions and 50 deletions

3
.gitmodules vendored
View file

@ -95,3 +95,6 @@
path = externals/pugixml path = externals/pugixml
url = https://github.com/zeux/pugixml.git url = https://github.com/zeux/pugixml.git
shallow = true shallow = true
[submodule "externals/discord-rpc"]
path = externals/discord-rpc
url = https://github.com/shadps4-emu/ext-discord-rpc

View file

@ -374,6 +374,8 @@ set(COMMON src/common/logging/backend.cpp
src/common/debug.h src/common/debug.h
src/common/decoder.cpp src/common/decoder.cpp
src/common/decoder.h src/common/decoder.h
src/common/discord_rpc_handler.cpp
src/common/discord_rpc_handler.h
src/common/elf_info.h src/common/elf_info.h
src/common/endian.h src/common/endian.h
src/common/enum.h src/common/enum.h
@ -858,3 +860,6 @@ if (UNIX AND NOT APPLE)
target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES}) target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
endif() endif()
endif() endif()
# Discord RPC
target_link_libraries(shadps4 PRIVATE discord-rpc)

View file

@ -184,5 +184,10 @@ if (NOT TARGET pugixml::pugixml)
add_subdirectory(pugixml) add_subdirectory(pugixml)
endif() endif()
# Discord RPC
set(BUILD_EXAMPLES OFF)
add_subdirectory(discord-rpc/)
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
# GCN Headers # GCN Headers
add_subdirectory(gcn) add_subdirectory(gcn)

1
externals/discord-rpc vendored Submodule

@ -0,0 +1 @@
Subproject commit 80d35b5f86adc6557d0384771119cf167495dbae

View file

@ -34,6 +34,7 @@ static bool isNeo = false;
static bool isFullscreen = false; static bool isFullscreen = false;
static bool playBGM = false; static bool playBGM = false;
static int BGMvolume = 50; static int BGMvolume = 50;
static bool enableDiscordRPC = false;
static u32 screenWidth = 1280; static u32 screenWidth = 1280;
static u32 screenHeight = 720; static u32 screenHeight = 720;
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
@ -98,6 +99,10 @@ int getBGMvolume() {
return BGMvolume; return BGMvolume;
} }
bool getEnableDiscordRPC() {
return enableDiscordRPC;
}
s16 getCursorState() { s16 getCursorState() {
return cursorState; return cursorState;
} }
@ -266,6 +271,10 @@ void setBGMvolume(int volume) {
BGMvolume = volume; BGMvolume = volume;
} }
void setEnableDiscordRPC(bool enable) {
enableDiscordRPC = enable;
}
void setCursorState(s16 newCursorState) { void setCursorState(s16 newCursorState) {
cursorState = newCursorState; cursorState = newCursorState;
} }
@ -452,6 +461,7 @@ void load(const std::filesystem::path& path) {
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false); isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
playBGM = toml::find_or<bool>(general, "playBGM", false); playBGM = toml::find_or<bool>(general, "playBGM", false);
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50); BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
logFilter = toml::find_or<std::string>(general, "logFilter", ""); logFilter = toml::find_or<std::string>(general, "logFilter", "");
logType = toml::find_or<std::string>(general, "logType", "sync"); logType = toml::find_or<std::string>(general, "logType", "sync");
userName = toml::find_or<std::string>(general, "userName", "shadPS4"); userName = toml::find_or<std::string>(general, "userName", "shadPS4");
@ -557,6 +567,7 @@ void save(const std::filesystem::path& path) {
data["General"]["Fullscreen"] = isFullscreen; data["General"]["Fullscreen"] = isFullscreen;
data["General"]["playBGM"] = playBGM; data["General"]["playBGM"] = playBGM;
data["General"]["BGMvolume"] = BGMvolume; data["General"]["BGMvolume"] = BGMvolume;
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
data["General"]["logFilter"] = logFilter; data["General"]["logFilter"] = logFilter;
data["General"]["logType"] = logType; data["General"]["logType"] = logType;
data["General"]["userName"] = userName; data["General"]["userName"] = userName;
@ -614,6 +625,7 @@ void setDefaultValues() {
isFullscreen = false; isFullscreen = false;
playBGM = false; playBGM = false;
BGMvolume = 50; BGMvolume = 50;
enableDiscordRPC = true;
cursorState = HideCursorState::Idle; cursorState = HideCursorState::Idle;
cursorHideTimeout = 5; cursorHideTimeout = 5;
screenWidth = 1280; screenWidth = 1280;

View file

@ -18,6 +18,7 @@ bool isNeoMode();
bool isFullscreenMode(); bool isFullscreenMode();
bool getPlayBGM(); bool getPlayBGM();
int getBGMvolume(); int getBGMvolume();
bool getEnableDiscordRPC();
s16 getCursorState(); s16 getCursorState();
int getCursorHideTimeout(); int getCursorHideTimeout();
@ -57,6 +58,7 @@ void setScreenHeight(u32 height);
void setFullscreenMode(bool enable); void setFullscreenMode(bool enable);
void setPlayBGM(bool enable); void setPlayBGM(bool enable);
void setBGMvolume(int volume); void setBGMvolume(int volume);
void setEnableDiscordRPC(bool enable);
void setCursorState(s16 cursorState); void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout); void setCursorHideTimeout(int newcursorHideTimeout);
void setLanguage(u32 language); void setLanguage(u32 language);

View file

@ -1,43 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <ctime>
#include "common/discord.h"
namespace Discord {
void RPC::init() {
DiscordEventHandlers handlers{};
Discord_Initialize("1139939140494971051", &handlers, 1, nullptr);
startTimestamp = time(nullptr);
enabled = true;
}
void RPC::update(Discord::RPCStatus status, const std::string& game) {
DiscordRichPresence rpc{};
if (status == Discord::RPCStatus::Playing) {
rpc.details = "Playing a game";
rpc.state = game.c_str();
} else {
rpc.details = "Idle";
}
rpc.largeImageKey = "shadps4";
rpc.largeImageText = "ShadPS4 is a PS4 emulator";
rpc.startTimestamp = startTimestamp;
Discord_UpdatePresence(&rpc);
}
void RPC::stop() {
if (enabled) {
enabled = false;
Discord_ClearPresence();
Discord_Shutdown();
}
}
} // namespace Discord

View file

@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <ctime>
#include "src/common/discord_rpc_handler.h"
namespace DiscordRPCHandler {
void RPC::init() {
DiscordEventHandlers handlers{};
Discord_Initialize("1138176975865909360", &handlers, 1, nullptr);
startTimestamp = time(nullptr);
rpcEnabled = true;
}
void RPC::setStatusIdling() {
DiscordRichPresence rpc{};
rpc.largeImageKey = "https://github.com/shadps4-emu/shadPS4/raw/main/.github/shadps4.png";
rpc.largeImageText = "shadPS4 is a PS4 emulator";
rpc.startTimestamp = startTimestamp;
rpc.details = "Idle";
status = RPCStatus::Idling;
Discord_UpdatePresence(&rpc);
}
void RPC::setStatusPlaying(const std::string& game_name, const std::string& game_id) {
DiscordRichPresence rpc{};
rpc.details = "Playing";
rpc.state = game_name.c_str();
std::string largeImageUrl =
"https://store.playstation.com/store/api/chihiro/00_09_000/titlecontainer/US/en/999/" +
game_id + "_00/image";
rpc.largeImageKey = largeImageUrl.c_str();
rpc.largeImageText = game_name.c_str();
rpc.startTimestamp = startTimestamp;
status = RPCStatus::Playing;
Discord_UpdatePresence(&rpc);
}
void RPC::shutdown() {
if (rpcEnabled) {
rpcEnabled = false;
Discord_ClearPresence();
Discord_Shutdown();
}
}
bool RPC::getRPCEnabled() {
return rpcEnabled;
}
} // namespace DiscordRPCHandler

View file

@ -7,7 +7,7 @@
#include <string> #include <string>
#include <discord_rpc.h> #include <discord_rpc.h>
namespace Discord { namespace DiscordRPCHandler {
enum class RPCStatus { enum class RPCStatus {
Idling, Idling,
@ -16,12 +16,15 @@ enum class RPCStatus {
class RPC { class RPC {
std::uint64_t startTimestamp; std::uint64_t startTimestamp;
bool enabled = false; bool rpcEnabled = false;
RPCStatus status;
public: public:
void init(); void init();
void update(RPCStatus status, const std::string& title); void setStatusIdling();
void stop(); void setStatusPlaying(const std::string& game_name, const std::string& game_id);
void shutdown();
bool getRPCEnabled();
}; };
} // namespace Discord } // namespace DiscordRPCHandler

View file

@ -11,6 +11,7 @@
#include "common/memory_patcher.h" #include "common/memory_patcher.h"
#endif #endif
#include "common/assert.h" #include "common/assert.h"
#include "common/discord_rpc_handler.h"
#include "common/elf_info.h" #include "common/elf_info.h"
#include "common/ntapi.h" #include "common/ntapi.h"
#include "common/path_util.h" #include "common/path_util.h"
@ -210,6 +211,15 @@ void Emulator::Run(const std::filesystem::path& file) {
} }
} }
// Discord RPC
if (Config::getEnableDiscordRPC()) {
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
if (rpc->getRPCEnabled() == false) {
rpc->init();
}
rpc->setStatusPlaying(game_info.title, id);
}
// start execution // start execution
std::jthread mainthread = std::jthread mainthread =
std::jthread([this](std::stop_token stop_token) { linker->Execute(); }); std::jthread([this](std::stop_token stop_token) { linker->Execute(); });

View file

@ -69,6 +69,14 @@ bool MainWindow::Init() {
QString statusMessage = QString statusMessage =
"Games: " + QString::number(numGames) + " (" + QString::number(duration.count()) + "ms)"; "Games: " + QString::number(numGames) + " (" + QString::number(duration.count()) + "ms)";
statusBar->showMessage(statusMessage); statusBar->showMessage(statusMessage);
// Initialize Discord RPC
if (Config::getEnableDiscordRPC()) {
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
rpc->init();
rpc->setStatusIdling();
}
return true; return true;
} }

View file

@ -9,6 +9,7 @@
#include "background_music_player.h" #include "background_music_player.h"
#include "common/config.h" #include "common/config.h"
#include "common/discord_rpc_handler.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "core/file_format/psf.h" #include "core/file_format/psf.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"

View file

@ -165,6 +165,17 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices, QWidge
BackgroundMusicPlayer::getInstance().setVolume(val); BackgroundMusicPlayer::getInstance().setVolume(val);
}); });
connect(ui->discordRPCCheckbox, &QCheckBox::stateChanged, this, [](int val) {
Config::setEnableDiscordRPC(val);
auto* rpc = Common::Singleton<DiscordRPCHandler::RPC>::Instance();
if (val == Qt::Checked) {
rpc->init();
rpc->setStatusIdling();
} else {
rpc->shutdown();
}
});
connect(ui->backButtonBehaviorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(ui->backButtonBehaviorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this](int index) { this, [this](int index) {
if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) {
@ -273,6 +284,7 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->nullGpuCheckBox->setChecked(Config::nullGpu()); ui->nullGpuCheckBox->setChecked(Config::nullGpu());
ui->playBGMCheckBox->setChecked(Config::getPlayBGM()); ui->playBGMCheckBox->setChecked(Config::getPlayBGM());
ui->BGMVolumeSlider->setValue((Config::getBGMvolume())); ui->BGMVolumeSlider->setValue((Config::getBGMvolume()));
ui->discordRPCCheckbox->setChecked(Config::getEnableDiscordRPC());
ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode()); ui->fullscreenCheckBox->setChecked(Config::isFullscreenMode());
ui->showSplashCheckBox->setChecked(Config::showSplash()); ui->showSplashCheckBox->setChecked(Config::showSplash());
ui->ps4proCheckBox->setChecked(Config::isNeoMode()); ui->ps4proCheckBox->setChecked(Config::isNeoMode());

View file

@ -148,6 +148,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="discordRPCCheckbox">
<property name="text">
<string>Enable Discord Rich Presence</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>