From 77c24ed7c33188eed06fab636afc0ab2675493bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20Gro=C3=9F?= Date: Mon, 30 Oct 2023 19:03:49 +0100 Subject: [PATCH] Initial version of Discord rich presence support. Co-authored-by: Maki Co-authored-by: Dale Glass --- cmake/macros/TargetDiscordRPC.cmake | 6 ++ .../discord-rpc/disable-downloading.patch | 21 +++++ cmake/ports/discord-rpc/portfile.cmake | 33 ++++++++ cmake/ports/discord-rpc/vcpkg.json | 18 +++++ cmake/ports/hifi-client-deps/CONTROL | 2 +- cmake/ports/rapidjson/portfile.cmake | 43 ++++++++++ cmake/ports/rapidjson/vcpkg.json | 17 ++++ interface/CMakeLists.txt | 1 + interface/src/Application.cpp | 3 + interface/src/Application.h | 3 + interface/src/DiscordRichPresence.cpp | 79 +++++++++++++++++++ interface/src/DiscordRichPresence.h | 36 +++++++++ 12 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 cmake/macros/TargetDiscordRPC.cmake create mode 100644 cmake/ports/discord-rpc/disable-downloading.patch create mode 100644 cmake/ports/discord-rpc/portfile.cmake create mode 100644 cmake/ports/discord-rpc/vcpkg.json create mode 100644 cmake/ports/rapidjson/portfile.cmake create mode 100644 cmake/ports/rapidjson/vcpkg.json create mode 100644 interface/src/DiscordRichPresence.cpp create mode 100644 interface/src/DiscordRichPresence.h diff --git a/cmake/macros/TargetDiscordRPC.cmake b/cmake/macros/TargetDiscordRPC.cmake new file mode 100644 index 0000000000..28b2fe79e7 --- /dev/null +++ b/cmake/macros/TargetDiscordRPC.cmake @@ -0,0 +1,6 @@ +macro(TARGET_DISCORD_RPC) + find_library(DISCORD_RPC_LIBRARY_RELEASE discord-rpc PATHS ${VCPKG_INSTALL_ROOT}/lib) + find_library(DISCORD_RPC_LIBRARY_DEBUG discord-rpc PATHS ${VCPKG_INSTALL_ROOT}/debug/lib) + select_library_configurations(DISCORD_RPC) + target_link_libraries(${TARGET_NAME} ${DISCORD_RPC_LIBRARY}) +endmacro() diff --git a/cmake/ports/discord-rpc/disable-downloading.patch b/cmake/ports/discord-rpc/disable-downloading.patch new file mode 100644 index 0000000000..2737e24e10 --- /dev/null +++ b/cmake/ports/discord-rpc/disable-downloading.patch @@ -0,0 +1,21 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 5dad9e9..961f02d 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -12,6 +12,7 @@ file(GLOB_RECURSE ALL_SOURCE_FILES + src/*.cpp src/*.h src/*.c + ) + ++if(0) + # Set CLANG_FORMAT_SUFFIX if you are using custom clang-format, e.g. clang-format-5.0 + find_program(CLANG_FORMAT_CMD clang-format${CLANG_FORMAT_SUFFIX}) + +@@ -43,7 +44,7 @@ if (NOT RAPIDJSONTEST) + ) + file(REMOVE ${RJ_TAR_FILE}) + endif(NOT RAPIDJSONTEST) +- ++endif() + find_file(RAPIDJSON NAMES rapidjson rapidjson-1.1.0 PATHS ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty CMAKE_FIND_ROOT_PATH_BOTH) + + add_library(rapidjson STATIC IMPORTED ${RAPIDJSON}) diff --git a/cmake/ports/discord-rpc/portfile.cmake b/cmake/ports/discord-rpc/portfile.cmake new file mode 100644 index 0000000000..6a60940844 --- /dev/null +++ b/cmake/ports/discord-rpc/portfile.cmake @@ -0,0 +1,33 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO discordapp/discord-rpc + REF v3.4.0 + SHA512 ca981b833aff5f21fd629a704deadd8e3fb5423d959ddb75e381313f6462d984c567671b10c8f031905c08d85792ddbe2dddc402ba2613c42de9e80fc68d0d51 + HEAD_REF master + PATCHES disable-downloading.patch +) + +string(COMPARE EQUAL "${VCPKG_CRT_LINKAGE}" "static" STATIC_CRT) +file(REMOVE_RECURSE "${SOURCE_PATH}/thirdparty") + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DUSE_STATIC_CRT=${STATIC_CRT} + -DBUILD_EXAMPLES=OFF + -DRAPIDJSONTEST=TRUE + "-DRAPIDJSON=${CURRENT_INSTALLED_DIR}" +) + +if(EXISTS ${SOURCE_PATH}/thirdparty) + message(FATAL_ERROR "The source directory should not be modified during the build.") +endif() + +vcpkg_cmake_install() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") + +# Copy copright information +file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/discord-rpc" RENAME "copyright") + +vcpkg_copy_pdbs() diff --git a/cmake/ports/discord-rpc/vcpkg.json b/cmake/ports/discord-rpc/vcpkg.json new file mode 100644 index 0000000000..44924ea701 --- /dev/null +++ b/cmake/ports/discord-rpc/vcpkg.json @@ -0,0 +1,18 @@ +{ + "name": "discord-rpc", + "version": "3.4.0", + "port-version": 3, + "description": "Rich Presence allows you to leverage the totally overhauled \"Now Playing\" section in a Discord user's profile to help people play your game together.", + "homepage": "https://github.com/discordapp/discord-rpc", + "dependencies": [ + "rapidjson", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/cmake/ports/hifi-client-deps/CONTROL b/cmake/ports/hifi-client-deps/CONTROL index 3a8c4693b3..afee6a5d48 100644 --- a/cmake/ports/hifi-client-deps/CONTROL +++ b/cmake/ports/hifi-client-deps/CONTROL @@ -1,4 +1,4 @@ Source: hifi-client-deps Version: 0.1 Description: Collected dependencies for High Fidelity applications -Build-Depends: hifi-deps, aristo (windows), glslang, liblo (windows), nlohmann-json, openvr ((linux&!arm)|windows), quazip (!android), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), sranipal (windows), vulkanmemoryallocator +Build-Depends: hifi-deps, aristo (windows), glslang, liblo (windows), nlohmann-json, openvr ((linux&!arm)|windows), quazip (!android), sdl2 (!android), spirv-cross (!android), spirv-tools (!android), sranipal (windows), vulkanmemoryallocator, discord-rpc (!android) diff --git a/cmake/ports/rapidjson/portfile.cmake b/cmake/ports/rapidjson/portfile.cmake new file mode 100644 index 0000000000..7c7c4e16dd --- /dev/null +++ b/cmake/ports/rapidjson/portfile.cmake @@ -0,0 +1,43 @@ +#header-only library +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO Tencent/rapidjson + REF a95e013b97ca6523f32da23f5095fcc9dd6067e5 # accessed on 2023-07-17 + SHA512 19bf9a579df70cbeaf60c7ccf25c92c327bffe95b0df14f27f2132134d5bb214e98a45e021eb287c4790e301f84bb095e0bdb3c97f65a37fbeb254970d97c005 + FILE_DISAMBIGUATOR 2 + HEAD_REF master +) + +# Use RapidJSON's own build process, skipping examples and tests +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DRAPIDJSON_BUILD_DOC=OFF + -DRAPIDJSON_BUILD_EXAMPLES=OFF + -DRAPIDJSON_BUILD_TESTS=OFF +) +vcpkg_cmake_install() + +if(VCPKG_TARGET_IS_WINDOWS) + vcpkg_cmake_config_fixup(CONFIG_PATH cmake) +else() + vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/RapidJSON) +endif() + +vcpkg_fixup_pkgconfig() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/share/doc") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include" "${CURRENT_PACKAGES_DIR}/debug/share") + +if(VCPKG_TARGET_IS_WINDOWS) + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug" "${CURRENT_PACKAGES_DIR}/lib") +endif() + +file(READ "${CURRENT_PACKAGES_DIR}/share/${PORT}/RapidJSONConfig.cmake" _contents) +string(REPLACE "\${RapidJSON_SOURCE_DIR}" "\${RapidJSON_CMAKE_DIR}/../.." _contents "${_contents}") +string(REPLACE "set( RapidJSON_SOURCE_DIR \"${SOURCE_PATH}\")" "" _contents "${_contents}") +string(REPLACE "set( RapidJSON_DIR \"${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel\")" "" _contents "${_contents}") +string(REPLACE "\${RapidJSON_CMAKE_DIR}/../../../include" "\${RapidJSON_CMAKE_DIR}/../../include" _contents "${_contents}") +file(WRITE "${CURRENT_PACKAGES_DIR}/share/${PORT}/RapidJSONConfig.cmake" "${_contents}\nset(RAPIDJSON_INCLUDE_DIRS \"\${RapidJSON_INCLUDE_DIRS}\")\n") + +file(INSTALL "${SOURCE_PATH}/license.txt" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) diff --git a/cmake/ports/rapidjson/vcpkg.json b/cmake/ports/rapidjson/vcpkg.json new file mode 100644 index 0000000000..ec17e082f5 --- /dev/null +++ b/cmake/ports/rapidjson/vcpkg.json @@ -0,0 +1,17 @@ +{ + "name": "rapidjson", + "version-date": "2023-07-17", + "description": "A fast JSON parser/generator for C++ with both SAX/DOM style API ", + "homepage": "http://rapidjson.org/", + "license": "MIT", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 7262ff0005..d68f14ca52 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -247,6 +247,7 @@ target_opengl() add_crashpad() target_breakpad() target_json() +target_discord_rpc() # perform standard include and linking for found externals foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6565d9c387..396bbd0c41 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2561,6 +2561,9 @@ Application::Application( DependencyManager::get()->preloadSounds(); DependencyManager::get()->createKeyboard(); + // Initialize Discord rich presence + _discordPresence = new DiscordPresence(); + FileDialogHelper::setOpenDirectoryOperator([this](const QString& path) { openDirectory(path); }); QDesktopServices::setUrlHandler("file", this, "showUrlHandler"); QDesktopServices::setUrlHandler("", this, "showUrlHandler"); diff --git a/interface/src/Application.h b/interface/src/Application.h index ced38120ae..d7a528a7e4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -79,6 +79,7 @@ #include "ui/OctreeStatsDialog.h" #include "ui/OverlayConductor.h" #include "ui/overlays/Overlays.h" +#include "DiscordRichPresence.h" #include "workload/GameWorkload.h" #include "graphics/GraphicsEngine.h" @@ -857,5 +858,7 @@ private: VisionSqueeze _visionSqueeze; bool _crashOnShutdown { false }; + + DiscordPresence* _discordPresence{ nullptr }; }; #endif // hifi_Application_h diff --git a/interface/src/DiscordRichPresence.cpp b/interface/src/DiscordRichPresence.cpp new file mode 100644 index 0000000000..b7611223d5 --- /dev/null +++ b/interface/src/DiscordRichPresence.cpp @@ -0,0 +1,79 @@ +// +// DiscordRichPresence.cpp +// interface/src +// +// Created by Julian Groß on 30th October 2023. +// Copyright 2023 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include +#include +#include + +#include "discord_rpc.h" +#include "DiscordRichPresence.h" +#include "DependencyManager.h" +#include "AddressManager.h" +#include "EntityTreeRenderer.h" + +#define DISCORD_APPLICATION_CLIENT_ID "1168082546270163014" +#define STEAM_APPLICATION_ID "1234" // placeholder since we don't have a Steam application ID yet + +Q_LOGGING_CATEGORY(discord_rich_presence, "overte.discord_rich_presence") + +DiscordPresence::DiscordPresence() +{ + DiscordEventHandlers handlers; + Discord_Initialize(DISCORD_APPLICATION_CLIENT_ID, &handlers, 1, STEAM_APPLICATION_ID); + const int64_t startEpoch = QDateTime::currentSecsSinceEpoch(); + discordPresence.startTimestamp = startEpoch; + auto addressManager = DependencyManager::get(); + connect(addressManager.data(), &AddressManager::hostChanged, this, &DiscordPresence::domainChanged); +} + +void DiscordPresence::shutdown() +{ + Discord_Shutdown(); +} + +void DiscordPresence::domainChanged() +{ + const auto addressManager = DependencyManager::get(); + if (!addressManager) return; + const auto entityTreeRenderer = DependencyManager::get(); + if (!entityTreeRenderer) return; + + // only continue if domain id changed or is serverless + bool isServerless = false; + const auto tree = entityTreeRenderer->getTree(); + if (tree) isServerless = tree->isServerlessMode(); + QString domainID = addressManager->getDomainID(); + if (currentDomainID == domainID && !isServerless) return; + currentDomainID = domainID; + + // get data + QString state; + // TODO: switch to getPlaceName once https://github.com/overte-org/overte/issues/684 is fixed + const QString worldName = addressManager->getHost(); + qCDebug(discord_rich_presence) << "Discord log hostName: " + worldName; + if (isServerless) { + state = "In a serverless world"; + } else { + state = ("In " + worldName); + } + + // create Discord presence payload + QByteArray state_data = state.toUtf8(); + discordPresence.state = state_data.constData(); + discordPresence.largeImageKey = "header"; + discordPresence.smallImageKey = "logo"; + + // update activity + Discord_UpdatePresence(&discordPresence); +} diff --git a/interface/src/DiscordRichPresence.h b/interface/src/DiscordRichPresence.h new file mode 100644 index 0000000000..91076fb5f1 --- /dev/null +++ b/interface/src/DiscordRichPresence.h @@ -0,0 +1,36 @@ +// +// DiscordRichPresence.h +// interface/src +// +// Created by Julian Groß on 30th October 2023. +// Copyright 2023 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef overte_DiscordPresence_h +#define overte_DiscordPresence_h + +#include "discord_rpc.h" +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(discord_rich_presence) + +class DiscordPresence : public QObject { + Q_OBJECT +public: + DiscordPresence(); + void shutdown(); + +public slots: + void domainChanged(); + +private: + QString currentDomainID; + DiscordRichPresence discordPresence{}; +}; + +#endif