diff --git a/CMakeLists.txt b/CMakeLists.txt index be1e5eeed..cc6a62f7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ option(ANTIBOT "Enable support for a dynamic anticheat library" OFF) option(CLIENT "Compile client" ON) option(DOWNLOAD_GTEST "Download and compile GTest" ${AUTO_DEPENDENCIES_DEFAULT}) option(STEAM "Build the Steam release version" OFF) +option(DISCORD "Enable discord rich presence support" OFF) option(PREFER_BUNDLED_LIBS "Prefer bundled libraries over system libraries" ${AUTO_DEPENDENCIES_DEFAULT}) option(DEV "Don't generate stuff necessary for packaging" OFF) @@ -368,6 +369,9 @@ find_package(Pnglite) find_package(PythonInterp 3) find_package(SDL2) find_package(SQLite3) +if(DISCORD) + find_package(DiscordSdk) +endif() if(UNIX) # Use -pthread instead of -lpthread to draw dependencies other than libpthread set(THREADS_PREFER_PTHREAD_FLAG TRUE) @@ -455,6 +459,9 @@ show_dependency_status("SDL2" SDL2) show_dependency_status("SQLite3" SQLite3) show_dependency_status("Wavpack" WAVPACK) show_dependency_status("Zlib" ZLIB) +if(DISCORD) + show_dependency_status("DiscordSdk" DiscordSdk) +endif() if(WEBSOCKETS) show_dependency_status("Websockets" WEBSOCKETS) endif() @@ -481,6 +488,10 @@ if(UPNP AND NOT(MINIUPNPC_FOUND)) message(SEND_ERROR "You must install miniupnpc to compile the DDNet server with UPnP support") endif() +if(DISCORD AND NOT(DISCORDSDK_FOUND)) + message(SEND_ERROR "You must install the discord sdk to compile the DDNet client with Discord support") +endif() + if(CLIENT AND NOT(FREETYPE_FOUND)) message(SEND_ERROR "You must install Freetype to compile the DDNet client") endif() @@ -1332,6 +1343,7 @@ set(COPY_FILES ${SQLite3_COPY_FILES} ${FFMPEG_COPY_FILES} ${WEBSOCKETS_COPY_FILES} + ${DISCORDSDK_COPY_FILES} ) file(COPY ${COPY_FILES} DESTINATION .) @@ -1630,6 +1642,13 @@ add_library(engine-shared EXCLUDE_FROM_ALL OBJECT ${ENGINE_INTERFACE} ${ENGINE_S add_library(game-shared EXCLUDE_FROM_ALL OBJECT ${GAME_SHARED} ${GAME_GENERATED_SHARED}) list(APPEND TARGETS_OWN engine-shared game-shared) +if(DISCORD) + add_library(discord-shared SHARED IMPORTED) + set_target_properties(discord-shared PROPERTIES + IMPORTED_LOCATION "${DISCORDSDK_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${DISCORDSDK_INCLUDE_DIRS}" + ) +endif() ######################################################################## # CLIENT @@ -1837,6 +1856,10 @@ if(CLIENT) ${CMAKE_THREAD_LIBS_INIT} ) + if(DISCORD) + list(APPEND LIBS_CLIENT discord-shared) + endif() + if(TARGET_OS STREQUAL "windows") set(CLIENT_ICON "other/icons/DDNet.rc") if(NOT MINGW) @@ -1873,6 +1896,7 @@ if(CLIENT) ${SDL2_INCLUDE_DIRS} ${WAVPACK_INCLUDE_DIRS} ${FFMPEG_INCLUDE_DIRS} + ${DISCORDSDK_INCLUDE_DIRS} ${PLATFORM_CLIENT_INCLUDE_DIRS} ) @@ -2657,6 +2681,9 @@ foreach(target ${TARGETS_OWN}) if(STEAM) target_compile_definitions(${target} PRIVATE PLATFORM_SUFFIX="-steam") endif() + if(DISCORD) + target_compile_definitions(${target} PRIVATE CONF_DISCORD) + endif() if(VERSION) target_compile_definitions(${target} PRIVATE GAME_RELEASE_VERSION="${VERSION}") endif() diff --git a/cmake/FindDiscordSdk.cmake b/cmake/FindDiscordSdk.cmake new file mode 100644 index 000000000..bf339dea8 --- /dev/null +++ b/cmake/FindDiscordSdk.cmake @@ -0,0 +1,36 @@ +set_extra_dirs_lib(DISCORDSDK discord) +find_library(DISCORDSDK_LIBRARY + NAMES discord_game_sdk.dll.lib discord_game_sdk.dylib discord_game_sdk.so + HINTS ${HINTS_DISCORDSDK_LIBDIR} + PATHS ${PATHS_DISCORDSDK_LIBDIR} + ${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH} +) +set_extra_dirs_include(DISCORDSDK discord "${DISCORDSDK_LIBRARY}") +find_path(DISCORDSDK_INCLUDEDIR discord_game_sdk.h + HINTS ${HINTS_DISCORDSDK_INCLUDEDIR} + PATHS ${PATHS_DISCORDSDK_INCLUDEDIR} + ${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DiscordSdk DEFAULT_MSG DISCORDSDK_LIBRARY DISCORDSDK_INCLUDEDIR) + +mark_as_advanced(DISCORDSDK_LIBRARY DISCORDSDK_INCLUDEDIR) + +if(DISCORDSDK_FOUND) + is_bundled(DISCORDSDK_BUNDLED "${DISCORDSDK_LIBRARY}") + set(DISCORDSDK_LIBRARIES ${DISCORDSDK_LIBRARY}) + set(DISCORDSDK_INCLUDE_DIRS ${DISCORDSDK_INCLUDEDIR}) + + if(DISCORDSDK_BUNDLED) + if(TARGET_OS STREQUAL "windows") + set(DISCORDSDK_COPY_FILES "${EXTRA_DISCORDSDK_LIBDIR}/discord_game_sdk.dll") + elseif(TARGET_OS STREQUAL "linux") + set(DISCORDSDK_COPY_FILES "${EXTRA_DISCORDSDK_LIBDIR}/discord_game_sdk.so") + elseif(TARGET_OS STREQUAL "mac") + set(DISCORDSDK_COPY_FILES "${EXTRA_DISCORDSDK_LIBDIR}/discord_game_sdk.bundle") + endif() + else() + set(DISCORDSDK_COPY_FILES) + endif() +endif() diff --git a/ddnet-libs b/ddnet-libs index f757a19a6..954e86cfc 160000 --- a/ddnet-libs +++ b/ddnet-libs @@ -1 +1 @@ -Subproject commit f757a19a6ee9806e4b8606a543c157a972493052 +Subproject commit 954e86cfcbfc3f90c71b0a177d10adf6ffb2c10c diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index a806f5086..803403d19 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -403,3 +403,7 @@ MACRO_CONFIG_INT(GfxEnableTextureUnitOptimization, gfx_enable_texture_unit_optim MACRO_CONFIG_INT(GfxUsePreinitBuffer, gfx_use_preinitialized_buffer, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Use only one buffer for data, that is uploaded to the GPU(might help when using an iGPUs).") MACRO_CONFIG_INT(ClVideoRecorderFPS, cl_video_recorder_fps, 60, 1, 1000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "At which FPS the videorecorder should record demos.") + +#if defined(CONF_DISCORD) +MACRO_CONFIG_INT(ClDiscordEnablePresence, cl_discord_enable_presence, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable discord rich presence.") +#endif diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 294b035f5..d7ff5837b 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -68,6 +68,10 @@ #include "components/race_demo.h" #include +#if defined(CONF_DISCORD) +#include +#endif + CGameClient g_GameClient; // instantiate all systems @@ -393,6 +397,27 @@ void CGameClient::OnInit() // Agressively try to grab window again since some Windows users report // window not being focussed after starting client. Graphics()->SetWindowGrab(true); + +#if defined(CONF_DISCORD) + mem_zero(&m_DiscordApp, sizeof(CDiscordApp)); + m_DiscordApp.m_ActivityEvents = (struct IDiscordActivityEvents *)malloc(sizeof(struct IDiscordActivityEvents)); + mem_zero(m_DiscordApp.m_ActivityEvents, sizeof(IDiscordActivityEvents)); + + struct DiscordCreateParams Params; + DiscordCreateParamsSetDefault(&Params); + Params.client_id = 752165779117441075; + Params.flags = EDiscordCreateFlags::DiscordCreateFlags_Default; + Params.event_data = &m_DiscordApp; + Params.activity_events = m_DiscordApp.m_ActivityEvents; + if(DiscordCreate(DISCORD_VERSION, &Params, &m_DiscordApp.m_Core) != DiscordResult_Ok) + dbg_msg("discord", "Error creating discord instance."); + else + dbg_msg("discord", "Created discord instance, version: %d", DISCORD_VERSION); + + m_DiscordApp.m_ActivityManager = m_DiscordApp.m_Core->get_activity_manager(m_DiscordApp.m_Core); + + SetDiscordActivity("In Menus", "Offline"); +#endif } void CGameClient::OnUpdate() @@ -422,6 +447,9 @@ void CGameClient::OnUpdate() break; } } +#if defined(CONF_DISCORD) + m_DiscordApp.m_Core->run_callbacks(m_DiscordApp.m_Core); +#endif } void CGameClient::OnDummySwap() @@ -913,6 +941,11 @@ void CGameClient::OnStateChange(int NewState, int OldState) if(NewState < IClient::STATE_ONLINE) OnReset(); +#if defined(CONF_DISCORD) + if(NewState == IClient::STATE_OFFLINE) + SetDiscordActivity("In Menus", "Offline"); +#endif + // then change the state for(int i = 0; i < m_All.m_Num; i++) m_All.m_paComponents[i]->OnStateChange(NewState, OldState); @@ -928,6 +961,11 @@ void CGameClient::OnShutdown() void CGameClient::OnEnterGame() { g_GameClient.m_pEffects->ResetDamageIndicator(); +#if defined(CONF_DISCORD) + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Playing %s", Client()->GetCurrentMap()); + SetDiscordActivity(aBuf, "Online"); +#endif } void CGameClient::OnGameOver() @@ -3006,3 +3044,24 @@ bool CGameClient::IsDisplayingWarning() { return m_pMenus->GetCurPopup() == CMenus::POPUP_WARNING; } + +#if defined(CONF_DISCORD) +void CGameClient::SetDiscordActivity(const char *pDetails, const char *pState) +{ + if(!g_Config.m_ClDiscordEnablePresence) + { + dbg_msg("discord", "rich presence disabled"); + m_DiscordApp.m_ActivityManager->clear_activity(m_DiscordApp.m_ActivityManager, 0, 0); + return; + } + struct DiscordActivity Activity; + mem_zero(&Activity, sizeof(DiscordActivity)); + str_copy(Activity.assets.large_image, "ddnet_logo", sizeof(Activity.assets.large_image)); + str_copy(Activity.assets.large_text, "DDNet logo", sizeof(Activity.assets.large_text)); + Activity.timestamps.start = time_timestamp(); + str_copy(Activity.details, pDetails, sizeof(Activity.details)); + str_copy(Activity.state, pState, sizeof(Activity.state)); + m_DiscordApp.m_ActivityManager->update_activity(m_DiscordApp.m_ActivityManager, &Activity, 0, 0); + dbg_msg("discord", "updated activity, details='%s' state='%s'", pDetails, pState); +} +#endif diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 828322d5e..b0f1c622b 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -95,6 +95,14 @@ class CGameClient : public IGameClient #if defined(CONF_AUTOUPDATE) class IUpdater *m_pUpdater; #endif +#if defined(CONF_DISCORD) + struct CDiscordApp + { + struct IDiscordCore *m_Core; + struct IDiscordActivityEvents *m_ActivityEvents; + struct IDiscordActivityManager *m_ActivityManager; + } m_DiscordApp; +#endif CLayers m_Layers; class CCollision m_Collision; @@ -354,6 +362,10 @@ public: void OnReset(); +#if defined(CONF_DISCORD) + void SetDiscordActivity(const char *pDetails, const char *pState); +#endif + // hooks virtual void OnConnected(); virtual void OnRender();