From fdaa3aca3e233c7aba69d0fd5f85e78288a4401a Mon Sep 17 00:00:00 2001 From: Kamil Schneider Date: Wed, 11 Oct 2023 03:06:57 +0200 Subject: [PATCH] Discord Rich Presence support (#3167) * get draft from #2577 * get draft from #2577 * add checkbox to allow connecting with discord-rpc test case with load data * safe delete only if used * fix vars when game is launching * add function to set appid and small fixes * added functions to manage assets, small fixes and bugs * rewriten setAsset, refactor littlebit * add function to check client connection with discord * add buttons to rpc * minor amendments, renaming of functions * remove update discord-rpc from build * thats too * Memory leak fixed, presence updated on app change * Presence state change, change of time variable * Fixed unable to change custom state * Added const to IsDiscordRPCEnabled * Added missing argument validation * Exclude vendor files, added premake script * Re-run actions * Bump discord-rpc version * Fix broken discord tag_name * Bump discord-rpc version * Revert to 2b42cd9 * Bump discord-rpc version * Disabled data customization if the app is not own * Change DEFAULT_APP_ID * Remove comment --------- Co-authored-by: Lpsd <40902730+Lpsd@users.noreply.github.com> --- Client/core/CClientVariables.cpp | 5 +- Client/core/CCore.cpp | 32 +++ Client/core/CCore.h | 50 ++-- Client/core/CDiscordRichPresence.cpp | 227 ++++++++++++++++ Client/core/CDiscordRichPresence.h | 66 +++++ Client/core/CMainMenu.cpp | 12 + Client/core/CSettings.cpp | 25 ++ Client/core/CSettings.h | 2 + Client/core/premake5.lua | 3 +- Client/mods/deathmatch/StdInc.h | 1 + Client/mods/deathmatch/logic/CClientGame.cpp | 8 + .../mods/deathmatch/logic/lua/CLuaManager.cpp | 1 + .../logic/luadefs/CLuaDiscordDefs.cpp | 192 ++++++++++++++ .../logic/luadefs/CLuaDiscordDefs.h | 31 +++ Client/sdk/core/CCoreInterface.h | 32 +-- Client/sdk/core/CDiscordInterface.h | 31 +++ premake5.lua | 2 + utils/buildactions/install_discord.lua | 250 ++++++++++++++++++ vendor/discord-rpc/.gitignore | 3 + vendor/discord-rpc/premake5.lua | 23 ++ win-create-projects.bat | 3 + 21 files changed, 958 insertions(+), 41 deletions(-) create mode 100644 Client/core/CDiscordRichPresence.cpp create mode 100644 Client/core/CDiscordRichPresence.h create mode 100644 Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp create mode 100644 Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h create mode 100644 Client/sdk/core/CDiscordInterface.h create mode 100644 utils/buildactions/install_discord.lua create mode 100644 vendor/discord-rpc/.gitignore create mode 100644 vendor/discord-rpc/premake5.lua diff --git a/Client/core/CClientVariables.cpp b/Client/core/CClientVariables.cpp index 85b471e412..4ceb4230b5 100644 --- a/Client/core/CClientVariables.cpp +++ b/Client/core/CClientVariables.cpp @@ -353,8 +353,9 @@ void CClientVariables::LoadDefaults() DEFAULT("browser_remote_websites", true); // Load remote websites? DEFAULT("browser_remote_javascript", true); // Execute javascript on remote websites? DEFAULT("filter_duplicate_log_lines", true); // Filter duplicate log lines for debug view and clientscript.log - DEFAULT("always_show_transferbox", false); // Should the transfer box always be visible for downloads? (and ignore scripted control) - DEFAULT("_beta_qc_rightclick_command", _S("reconnect")); // Command to run when right clicking quick connect (beta - can be removed at any time) + DEFAULT("always_show_transferbox", false); // Should the transfer box always be visible for downloads? (and ignore scripted control) + DEFAULT("allow_discord_rpc", false); // Enable Discord Rich Presence + DEFAULT("_beta_qc_rightclick_command", _S("reconnect")); // Command to run when right clicking quick connect (beta - can be removed at any time) if (!Exists("locale")) { diff --git a/Client/core/CCore.cpp b/Client/core/CCore.cpp index c14dcf7a03..5cd37cefa6 100644 --- a/Client/core/CCore.cpp +++ b/Client/core/CCore.cpp @@ -25,6 +25,7 @@ #include "CModelCacheManager.h" #include #include +#include "CDiscordRichPresence.h" using SharedUtil::CalcMTASAPath; using namespace std; @@ -160,12 +161,19 @@ CCore::CCore() // Create tray icon m_pTrayIcon = new CTrayIcon(); + + // Create discord rich presence + m_pDiscordRichPresence = std::shared_ptr(new CDiscordRichPresence()); } CCore::~CCore() { WriteDebugEvent("CCore::~CCore"); + // Reset Discord rich presence + if (m_pDiscordRichPresence) + m_pDiscordRichPresence.reset(); + // Destroy tray icon delete m_pTrayIcon; @@ -654,6 +662,20 @@ void CCore::SetConnected(bool bConnected) { m_pLocalGUI->GetMainMenu()->SetIsIngame(bConnected); UpdateIsWindowMinimized(); // Force update of stuff + + if (g_pCore->GetCVars()->GetValue("allow_discord_rpc", false)) + { + auto discord = g_pCore->GetDiscord(); + if (!discord->IsDiscordRPCEnabled()) + discord->SetDiscordRPCEnabled(true); + + discord->SetPresenceState(bConnected ? "In-game" : "Main menu", false); + discord->SetPresenceStartTimestamp(0); + discord->SetPresenceDetails(""); + + if (bConnected) + discord->SetPresenceStartTimestamp(time(nullptr)); + } } bool CCore::IsConnected() @@ -1315,6 +1337,10 @@ void CCore::DoPostFramePulse() GetGraphStats()->Draw(); m_pConnectManager->DoPulse(); + static auto discord = g_pCore->GetDiscord(); + if (discord && discord->IsDiscordRPCEnabled()) + discord->UpdatePresence(); + TIMING_CHECKPOINT("-CorePostFrame2"); } @@ -2361,3 +2387,9 @@ size_t CCore::GetStreamingMemory() ? m_CustomStreamingMemoryLimitBytes : CVARS_GET_VALUE("streaming_memory") * 1024 * 1024; // MB to B conversion } + +// Discord rich presence +std::shared_ptr CCore::GetDiscord() +{ + return m_pDiscordRichPresence; +} diff --git a/Client/core/CCore.h b/Client/core/CCore.h index 698af7e172..5d7639a0f6 100644 --- a/Client/core/CCore.h +++ b/Client/core/CCore.h @@ -10,6 +10,8 @@ *****************************************************************************/ class CCore; +class CDiscordRichPresence; +class CDiscordInterface; #pragma once @@ -82,25 +84,26 @@ class CCore : public CCoreInterface, public CSingleton ~CCore(); // Subsystems (query) - eCoreVersion GetVersion(); - CConsoleInterface* GetConsole(); - CCommandsInterface* GetCommands(); - CConnectManager* GetConnectManager() { return m_pConnectManager; }; - CGame* GetGame(); - CGUI* GetGUI(); - CGraphicsInterface* GetGraphics(); - CModManagerInterface* GetModManager(); - CMultiplayer* GetMultiplayer(); - CNet* GetNetwork(); - CXML* GetXML() { return m_pXML; }; - CXMLNode* GetConfig(); - CClientVariables* GetCVars() { return &m_ClientVariables; }; - CKeyBindsInterface* GetKeyBinds(); - CMouseControl* GetMouseControl() { return m_pMouseControl; }; - CLocalGUI* GetLocalGUI(); - CLocalizationInterface* GetLocalization() { return g_pLocalization; }; - CWebCoreInterface* GetWebCore(); - CTrayIconInterface* GetTrayIcon() { return m_pTrayIcon; }; + eCoreVersion GetVersion(); + CConsoleInterface* GetConsole(); + CCommandsInterface* GetCommands(); + CConnectManager* GetConnectManager() { return m_pConnectManager; }; + CGame* GetGame(); + CGUI* GetGUI(); + CGraphicsInterface* GetGraphics(); + CModManagerInterface* GetModManager(); + CMultiplayer* GetMultiplayer(); + CNet* GetNetwork(); + CXML* GetXML() { return m_pXML; }; + CXMLNode* GetConfig(); + CClientVariables* GetCVars() { return &m_ClientVariables; }; + CKeyBindsInterface* GetKeyBinds(); + CMouseControl* GetMouseControl() { return m_pMouseControl; }; + CLocalGUI* GetLocalGUI(); + CLocalizationInterface* GetLocalization() { return g_pLocalization; }; + CWebCoreInterface* GetWebCore(); + CTrayIconInterface* GetTrayIcon() { return m_pTrayIcon; }; + std::shared_ptr GetDiscord(); void SaveConfig(bool bWaitUntilFinished = false); @@ -296,10 +299,11 @@ class CCore : public CCoreInterface, public CSingleton CModelCacheManager* m_pModelCacheManager; // Instances (put new classes here!) - CXMLFile* m_pConfigFile; - CClientVariables m_ClientVariables; - CWebCoreInterface* m_pWebCore = nullptr; - CTrayIcon* m_pTrayIcon; + CXMLFile* m_pConfigFile; + CClientVariables m_ClientVariables; + CWebCoreInterface* m_pWebCore = nullptr; + CTrayIcon* m_pTrayIcon; + std::shared_ptr m_pDiscordRichPresence; // Hook interfaces. CMessageLoopHook* m_pMessageLoopHook; diff --git a/Client/core/CDiscordRichPresence.cpp b/Client/core/CDiscordRichPresence.cpp new file mode 100644 index 0000000000..686fa89d21 --- /dev/null +++ b/Client/core/CDiscordRichPresence.cpp @@ -0,0 +1,227 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: core/CDiscordRichPresence.cpp + * PURPOSE: Discord rich presence implementation + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "discord_rpc.h" +#include "CDiscordRichPresence.h" + +constexpr char DEFAULT_APP_ID[] = "468493322583801867"; +constexpr char DEFAULT_APP_ASSET[] = "mta_logo_round"; +constexpr char DEFAULT_APP_ASSET_TEXT[] = "Multi Theft Auto"; +constexpr char DEFAULT_APP_ASSET_SMALL[] = ""; +constexpr char DEFAULT_APP_ASSET_SMALL_TEXT[] = ""; + +CDiscordRichPresence::CDiscordRichPresence() : m_uiDiscordAppStart(0), m_uiDiscordAppEnd(0) +{ + SetDefaultData(); + + m_strDiscordAppState.clear(); +} + +CDiscordRichPresence::~CDiscordRichPresence() +{ + if (m_bDiscordRPCEnabled) + ShutdownDiscord(); +} + +void CDiscordRichPresence::InitializeDiscord() +{ + DiscordEventHandlers handlers; + memset(&handlers, 0, sizeof(handlers)); + + // Handlers .ready .disconnected .errored maybe use in future? + Discord_Initialize((m_strDiscordAppCurrentId.empty()) ? DEFAULT_APP_ID : m_strDiscordAppCurrentId.c_str(), &handlers, 1, nullptr); + + m_bDisallowCustomDetails = (m_strDiscordAppCurrentId == DEFAULT_APP_ID) ? true : false; +} + +void CDiscordRichPresence::ShutdownDiscord() +{ + Discord_Shutdown(); +} + +void CDiscordRichPresence::RestartDiscord() +{ + ShutdownDiscord(); + InitializeDiscord(); +} + +void CDiscordRichPresence::SetDefaultData() +{ + m_strDiscordAppId = DEFAULT_APP_ID; + m_strDiscordAppAsset = DEFAULT_APP_ASSET; + m_strDiscordAppAssetText = DEFAULT_APP_ASSET_TEXT; + + m_strDiscordAppAssetSmall = DEFAULT_APP_ASSET_SMALL; + m_strDiscordAppAssetSmallText = DEFAULT_APP_ASSET_SMALL_TEXT; + + m_strDiscordAppCurrentId = DEFAULT_APP_ID; + m_strDiscordAppDetails.clear(); + m_strDiscordAppCustomState.clear(); + + m_aButtons = {}; + m_bUpdateRichPresence = true; + m_bDisallowCustomDetails = true; +} + +void CDiscordRichPresence::UpdatePresence() +{ + if (!m_bUpdateRichPresence) + return; + + DiscordRichPresence discordPresence; + memset(&discordPresence, 0, sizeof(discordPresence)); + + discordPresence.largeImageKey = m_strDiscordAppAsset.c_str(); + discordPresence.largeImageText = m_strDiscordAppAssetText.c_str(); + discordPresence.smallImageKey = m_strDiscordAppAssetSmall.c_str(); + discordPresence.smallImageText = m_strDiscordAppAssetSmallText.c_str(); + + discordPresence.state = (!m_strDiscordAppCustomState.empty() || !m_bDisallowCustomDetails) ? m_strDiscordAppCustomState.c_str() : m_strDiscordAppState.c_str(); + + discordPresence.details = m_strDiscordAppDetails.c_str(); + discordPresence.startTimestamp = m_uiDiscordAppStart; + + DiscordButton buttons[2]; + if (m_aButtons) + { + buttons[0].label = std::get<0>(*m_aButtons).first.c_str(); + buttons[0].url = std::get<0>(*m_aButtons).second.c_str(); + buttons[1].label = std::get<1>(*m_aButtons).first.c_str(); + buttons[1].url = std::get<1>(*m_aButtons).second.c_str(); + + discordPresence.buttons = buttons; + } + + Discord_UpdatePresence(&discordPresence); + m_bUpdateRichPresence = false; +} + +void CDiscordRichPresence::SetPresenceStartTimestamp(const unsigned long ulStart) +{ + m_uiDiscordAppStart = ulStart; + m_bUpdateRichPresence = true; +} + +void CDiscordRichPresence::SetAssetLargeData(const char* szAsset, const char* szAssetText) +{ + SetAsset(szAsset, szAssetText, true); +} + +void CDiscordRichPresence::SetAssetSmallData(const char* szAsset, const char* szAssetText) +{ + SetAsset(szAsset, szAssetText, false); +} + +void CDiscordRichPresence::SetAsset(const char* szAsset, const char* szAssetText, bool isLarge) +{ + if (isLarge) + { + m_strDiscordAppAsset = (szAsset && *szAsset) ? szAsset : DEFAULT_APP_ASSET; + m_strDiscordAppAssetText = (szAssetText && *szAssetText) ? szAssetText : DEFAULT_APP_ASSET_TEXT; + } + else + { + m_strDiscordAppAssetSmall = (szAsset && *szAsset) ? szAsset : DEFAULT_APP_ASSET_SMALL; + m_strDiscordAppAssetSmallText = (szAssetText && *szAssetText) ? szAssetText : DEFAULT_APP_ASSET_SMALL_TEXT; + } + m_bUpdateRichPresence = true; +} + +bool CDiscordRichPresence::SetPresenceState(const char* szState, bool bCustom) +{ + if (bCustom) + m_strDiscordAppCustomState = szState; + else + m_strDiscordAppState = szState; + + m_bUpdateRichPresence = true; + return true; +} + +bool CDiscordRichPresence::SetPresenceButtons(unsigned short int iIndex, const char* szName, const char* szUrl) +{ + // Should it always return true? + if (iIndex <= 2) + { + std::decay_t buttons; + if (m_aButtons) + buttons = *m_aButtons; + + if (iIndex == 1) + std::get<0>(buttons) = {szName, szUrl}; + else if (iIndex == 2) + std::get<1>(buttons) = {szName, szUrl}; + + m_aButtons = buttons; + m_bUpdateRichPresence = true; + + return true; + } + + return false; +} + +bool CDiscordRichPresence::SetPresenceDetails(const char* szDetails, bool bCustom) +{ + m_strDiscordAppDetails = szDetails; + m_bUpdateRichPresence = true; + return true; +} + +bool CDiscordRichPresence::ResetDiscordData() +{ + SetDefaultData(); + + if (m_bDiscordRPCEnabled) + { + RestartDiscord(); + m_bUpdateRichPresence = true; + } + return true; +} + +bool CDiscordRichPresence::SetApplicationID(const char* szAppID) +{ + m_strDiscordAppCurrentId = (szAppID && *szAppID) ? szAppID : DEFAULT_APP_ID; + + if (m_bDiscordRPCEnabled) + { + RestartDiscord(); + m_bUpdateRichPresence = true; + } + return true; +} + +bool CDiscordRichPresence::SetDiscordRPCEnabled(bool bEnabled) +{ + m_bDiscordRPCEnabled = bEnabled; + + if (!bEnabled) + { + ShutdownDiscord(); + return true; + } + + InitializeDiscord(); + m_bUpdateRichPresence = true; + return true; +} + +bool CDiscordRichPresence::IsDiscordRPCEnabled() const +{ + return m_bDiscordRPCEnabled; +} + +bool CDiscordRichPresence::IsDiscordCustomDetailsDisallowed() const +{ + return m_bDisallowCustomDetails; +} diff --git a/Client/core/CDiscordRichPresence.h b/Client/core/CDiscordRichPresence.h new file mode 100644 index 0000000000..5301b12959 --- /dev/null +++ b/Client/core/CDiscordRichPresence.h @@ -0,0 +1,66 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: core/CDiscordRichPresence.h + * PURPOSE: Header file for discord rich presence + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ +#pragma once + +#include +#include + +class CDiscordRichPresence : public CDiscordInterface +{ +public: + CDiscordRichPresence(); + ~CDiscordRichPresence(); + + void InitializeDiscord(); + void ShutdownDiscord(); + void RestartDiscord(); + void SetDefaultData(); + + void UpdatePresence(); + void SetPresenceStartTimestamp(const unsigned long ulStart); + void SetAsset(const char* szAsset, const char* szAssetText, bool bIsLarge = false); + void SetAssetLargeData(const char* szAsset, const char* szAssetText); + void SetAssetSmallData(const char* szAsset, const char* szAssetText); + + bool ResetDiscordData(); + bool SetPresenceState(const char* szState, bool bCustom = false); + bool SetPresenceDetails(const char* szDetails, bool bCustom = false); + bool SetPresenceButtons(unsigned short int iIndex, const char* szName, const char* szUrl); + bool SetDiscordRPCEnabled(bool bEnabled); + bool IsDiscordCustomDetailsDisallowed() const; + bool IsDiscordRPCEnabled() const; + bool SetApplicationID(const char* szAppID); + + // void SetPresenceTimestamp(); + // void SetPresenceImage(); + // void SetPresenceText(); + +private: + std::string m_strDiscordAppId; + std::string m_strDiscordAppAsset; + std::string m_strDiscordAppAssetText; + std::string m_strDiscordAppAssetSmall; + std::string m_strDiscordAppAssetSmallText; + + std::string m_strDiscordAppCurrentId; + std::string m_strDiscordAppState; + std::string m_strDiscordAppDetails; + std::string m_strDiscordAppCustomState; + + std::optional, std::pair>> m_aButtons; + + unsigned long m_uiDiscordAppStart; + unsigned long m_uiDiscordAppEnd; + + bool m_bDisallowCustomDetails; + bool m_bDiscordRPCEnabled; + bool m_bUpdateRichPresence; +}; diff --git a/Client/core/CMainMenu.cpp b/Client/core/CMainMenu.cpp index f1b0b03d91..eeca79395c 100644 --- a/Client/core/CMainMenu.cpp +++ b/Client/core/CMainMenu.cpp @@ -13,6 +13,7 @@ #include #include "CNewsBrowser.h" #include "CLanguageSelector.h" +#include "CDiscordRichPresence.h" #define NATIVE_RES_X 1280.0f #define NATIVE_RES_Y 1024.0f @@ -293,6 +294,17 @@ CMainMenu::CMainMenu(CGUI* pManager) // We're not ingame SetIsIngame(false); + // Discord + if (g_pCore->GetCVars()->GetValue("allow_discord_rpc", false)) + { + auto discord = g_pCore->GetDiscord(); + if (!discord->IsDiscordRPCEnabled()) + discord->SetDiscordRPCEnabled(true); + + discord->SetPresenceState("Main menu", false); + discord->SetPresenceStartTimestamp(0); + } + // Store the pointer to the graphics subsystem m_pGraphics = CGraphics::GetSingletonPtr(); diff --git a/Client/core/CSettings.cpp b/Client/core/CSettings.cpp index 74f00d9f5c..369b67c4aa 100644 --- a/Client/core/CSettings.cpp +++ b/Client/core/CSettings.cpp @@ -388,6 +388,11 @@ void CSettings::CreateGUI() m_pCheckBoxAlwaysShowTransferBox->GetPosition(vecTemp, false); m_pCheckBoxAlwaysShowTransferBox->AutoSize(nullptr, 20.0f); + m_pCheckBoxAllowDiscordRPC = reinterpret_cast(pManager->CreateCheckBox(pTabMultiplayer, _("Allow connecting with Discord Rich Presence"), false)); + m_pCheckBoxAllowDiscordRPC->SetPosition(CVector2D(vecTemp.fX, vecTemp.fY + 20.0f)); + m_pCheckBoxAllowDiscordRPC->GetPosition(vecTemp, false); + m_pCheckBoxAllowDiscordRPC->AutoSize(NULL, 20.0f); + m_pCheckBoxCustomizedSAFiles = reinterpret_cast(pManager->CreateCheckBox(pTabMultiplayer, _("Use customized GTA:SA files"), true)); m_pCheckBoxCustomizedSAFiles->SetPosition(CVector2D(vecTemp.fX, vecTemp.fY + 20.0f)); m_pCheckBoxCustomizedSAFiles->GetPosition(vecTemp, false); @@ -1252,6 +1257,7 @@ void CSettings::CreateGUI() m_pCheckBoxVolumetricShadows->SetClickHandler(GUI_CALLBACK(&CSettings::OnVolumetricShadowsClick, this)); m_pCheckBoxAllowScreenUpload->SetClickHandler(GUI_CALLBACK(&CSettings::OnAllowScreenUploadClick, this)); m_pCheckBoxAllowExternalSounds->SetClickHandler(GUI_CALLBACK(&CSettings::OnAllowExternalSoundsClick, this)); + m_pCheckBoxAllowDiscordRPC->SetClickHandler(GUI_CALLBACK(&CSettings::OnAllowDiscordRPC, this)); m_pCheckBoxCustomizedSAFiles->SetClickHandler(GUI_CALLBACK(&CSettings::OnCustomizedSAFilesClick, this)); m_pCheckBoxWindowed->SetClickHandler(GUI_CALLBACK(&CSettings::OnWindowedClick, this)); m_pCheckBoxDPIAware->SetClickHandler(GUI_CALLBACK(&CSettings::OnDPIAwareClick, this)); @@ -3010,6 +3016,11 @@ void CSettings::LoadData() CVARS_GET("always_show_transferbox", alwaysShowTransferBox); m_pCheckBoxAlwaysShowTransferBox->SetSelected(alwaysShowTransferBox); + // Allow DiscordRPC + bool bAllowDiscordRPC; + CVARS_GET("allow_discord_rpc", bAllowDiscordRPC); + m_pCheckBoxAllowDiscordRPC->SetSelected(bAllowDiscordRPC); + // Customized sa files m_pCheckBoxCustomizedSAFiles->SetSelected(GetApplicationSettingInt("customized-sa-files-request") != 0); m_pCheckBoxCustomizedSAFiles->SetVisible(GetApplicationSettingInt("customized-sa-files-show") != 0); @@ -3436,6 +3447,11 @@ void CSettings::SaveData() CVARS_SET("always_show_transferbox", alwaysShowTransferBox); g_pCore->GetModManager()->TriggerCommand(mtasa::CMD_ALWAYS_SHOW_TRANSFERBOX, alwaysShowTransferBox); + // Allow DiscordRPC + bool bAllowDiscordRPC = m_pCheckBoxAllowDiscordRPC->GetSelected(); + CVARS_SET("allow_discord_rpc", bAllowDiscordRPC); + g_pCore->GetDiscord()->SetDiscordRPCEnabled(bAllowDiscordRPC); + // Grass bool bGrassEnabled = m_pCheckBoxGrass->GetSelected(); CVARS_SET("grass", bGrassEnabled); @@ -4474,6 +4490,15 @@ bool CSettings::OnAllowExternalSoundsClick(CGUIElement* pElement) return true; } +// +// DiscordRPC +// +bool CSettings::OnAllowDiscordRPC(CGUIElement* pElement) +{ + g_pCore->GetDiscord()->SetDiscordRPCEnabled(m_pCheckBoxAllowDiscordRPC->GetSelected()); + return true; +} + // // CustomizedSAFiles // diff --git a/Client/core/CSettings.h b/Client/core/CSettings.h index c1e3038ab4..495eb2a01b 100644 --- a/Client/core/CSettings.h +++ b/Client/core/CSettings.h @@ -154,6 +154,7 @@ class CSettings CGUICheckBox* m_pCheckBoxAllowScreenUpload; CGUICheckBox* m_pCheckBoxAllowExternalSounds; CGUICheckBox* m_pCheckBoxCustomizedSAFiles; + CGUICheckBox* m_pCheckBoxAllowDiscordRPC; CGUICheckBox* m_pCheckBoxAlwaysShowTransferBox; CGUICheckBox* m_pCheckBoxGrass; CGUICheckBox* m_pCheckBoxHeatHaze; @@ -389,6 +390,7 @@ class CSettings bool OnVolumetricShadowsClick(CGUIElement* pElement); bool OnAllowScreenUploadClick(CGUIElement* pElement); bool OnAllowExternalSoundsClick(CGUIElement* pElement); + bool OnAllowDiscordRPC(CGUIElement* pElement); bool OnCustomizedSAFilesClick(CGUIElement* pElement); bool ShowUnsafeResolutionsClick(CGUIElement* pElement); bool OnWindowedClick(CGUIElement* pElement); diff --git a/Client/core/premake5.lua b/Client/core/premake5.lua index 54a852852f..971e757271 100644 --- a/Client/core/premake5.lua +++ b/Client/core/premake5.lua @@ -20,6 +20,7 @@ project "Client Core" "../../vendor/pthreads/include", "../../vendor/sparsehash/src/", "../../vendor/detours/4.0.1/src", + "../../vendor/discord-rpc/discord/include", } pchheader "StdInc.h" @@ -46,7 +47,7 @@ project "Client Core" links { "ws2_32", "d3dx9", "Userenv", "DbgHelp", "xinput", "Imagehlp", "dxguid", "dinput8", "strmiids", "odbc32", "odbccp32", "shlwapi", "winmm", "gdi32", "Imm32", "Psapi", - "pthread", "libpng", "jpeg", "zlib", "tinygettext", + "pthread", "libpng", "jpeg", "zlib", "tinygettext", "discord-rpc", } defines { diff --git a/Client/mods/deathmatch/StdInc.h b/Client/mods/deathmatch/StdInc.h index d9048a57f7..0d7cc5168c 100644 --- a/Client/mods/deathmatch/StdInc.h +++ b/Client/mods/deathmatch/StdInc.h @@ -120,6 +120,7 @@ #include #include #include +#include #include #include #include diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 8727767ad7..c94ca60e6d 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -487,6 +487,14 @@ CClientGame::~CClientGame() g_pCore->ForceCursorVisible(false); SetCursorEventsEnabled(false); + // Reset discord stuff + auto discord = g_pCore->GetDiscord(); + if (discord && discord->IsDiscordRPCEnabled()) + { + discord->ResetDiscordData(); + discord->UpdatePresence(); + } + // Destroy our stuff SAFE_DELETE(m_pManager); // Will trigger onClientResourceStop SAFE_DELETE(m_pNametags); diff --git a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp index 0076c4d578..33388a5edc 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaManager.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaManager.cpp @@ -279,4 +279,5 @@ void CLuaManager::LoadCFunctions() CLuaWorldDefs::LoadFunctions(); CLuaXMLDefs::LoadFunctions(); CLuaClientDefs::LoadFunctions(); + CLuaDiscordDefs::LoadFunctions(); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp new file mode 100644 index 0000000000..74a3cd38bf --- /dev/null +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp @@ -0,0 +1,192 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include + +void CLuaDiscordDefs::LoadFunctions() +{ + // Backwards compatibility functions + constexpr static const std::pair functions[]{ + {"setDiscordApplicationID", ArgumentParser}, + {"setDiscordRichPresenceDetails", ArgumentParser}, + {"setDiscordRichPresenceState", ArgumentParser}, + {"setDiscordRichPresenceAsset", ArgumentParser}, + {"setDiscordRichPresenceSmallAsset", ArgumentParser}, + {"setDiscordRichPresenceButton", ArgumentParser}, + {"resetDiscordRichPresenceData", ArgumentParser}, + {"isDiscordRichPresenceConnected", ArgumentParser < IsDiscordRPCConnected>}, + + }; + + // Add functions + for (const auto& [name, func] : functions) + CLuaCFunctions::AddFunction(name, func); +} + +void CLuaDiscordDefs::AddClass(lua_State* luaVM) +{ + lua_newclass(luaVM); + + lua_classfunction(luaVM, "setApplication", "setDiscordApplicationID"); + lua_classfunction(luaVM, "setState", "setDiscordRichPresenceState"); + lua_classfunction(luaVM, "setDetails", "setDiscordRichPresenceDetails"); + lua_classfunction(luaVM, "setAsset", "setDiscordRichPresenceAsset"); + lua_classfunction(luaVM, "setSmallAsset", "setDiscordRichPresenceSmallAsset"); + lua_classfunction(luaVM, "setButton", "setDiscordRichPresenceButton"); + + lua_classfunction(luaVM, "isConnected", "isDiscordRichPresenceConnected"); + //lua_classfunction(luaVM, "setAppID", "setDiscordRichPresenceAppID"); + //lua_classfunction(luaVM, "setAppID", "setDiscordRichPresenceAppID"); + + lua_registerclass(luaVM, "DiscordRPC"); +} + +bool CLuaDiscordDefs::ResetData() +{ + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled() || !discord->ResetDiscordData()) + return false; + + return true; +} + +bool CLuaDiscordDefs::SetState(std::string strState) +{ + int stateLength = strState.length(); + + if (stateLength < 1 || stateLength > 128) + throw std::invalid_argument("State name must be greater than 0, or less than/equal to 128"); + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled()) + return false; + + if (discord->IsDiscordCustomDetailsDisallowed()) + return false; + + if (!discord->SetPresenceState(strState.c_str(), true)) + return false; + + return true; +} + +bool CLuaDiscordDefs::SetAppID(std::string strAppID) +{ + int appIDLength = strAppID.length(); + + if (appIDLength < 1 || appIDLength > 32) + throw std::invalid_argument("Application ID must be greater than 0, or less than/equal to 32"); + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled() || !discord->SetApplicationID(strAppID.c_str())) + return false; + + return true; +} + +bool CLuaDiscordDefs::SetDetails(std::string strDetails) +{ + int detailsLength = strDetails.length(); + + if (detailsLength < 1 || detailsLength > 128) + throw std::invalid_argument("Details length must be greater than 0, or less than/equal to 128"); + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled()) + return false; + + if (discord->IsDiscordCustomDetailsDisallowed()) + return false; + + if (!discord->SetPresenceDetails(strDetails.c_str())) + return false; + + return true; +} + +bool CLuaDiscordDefs::SetAsset(std::string szAsset, std::string szAssetText, bool bIsLarge) +{ + int assetLength = szAsset.length(); + int assetTextLength = szAssetText.length(); + + if (assetLength < 1 || assetLength > 32) + throw std::invalid_argument("Asset name length must be greater than 0, or less than/equal to 32"); + if (assetTextLength < 1 || assetTextLength > 128) + throw std::invalid_argument("Asset text length must be greater than 0, or less than/equal to 128"); + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled()) + return false; + + if (discord->IsDiscordCustomDetailsDisallowed()) + return false; + + if (bIsLarge) + discord->SetAssetLargeData(szAsset.c_str(), szAssetText.c_str()); + else + discord->SetAssetSmallData(szAsset.c_str(), szAssetText.c_str()); + + return true; +} + +bool CLuaDiscordDefs::SetLargeAsset(std::string szAsset, std::string szAssetText) +{ + return SetAsset(szAsset, szAssetText, true); +} + +bool CLuaDiscordDefs::SetSmallAsset(std::string szAsset, std::string szAssetText) +{ + return SetAsset(szAsset, szAssetText, false); +} + +bool CLuaDiscordDefs::SetButtons(unsigned short int iIndex, std::string szName, std::string szUrl) +{ + if (iIndex < 1 || iIndex > 2) + return false; + + int nameLength = szName.length(); + int urlLength = szUrl.length(); + + if (nameLength < 1 || nameLength > 32) + throw std::invalid_argument("Button name length must be greater than 0, or less than/equal to 32"); + if (urlLength < 1 || urlLength > 128) + throw std::invalid_argument("Button URL length must be greater than 0, or less than/equal to 128"); + + if (szUrl.find("https://") != 0 && szUrl.find("mtasa://") != 0) + throw std::invalid_argument("Button URL should include the https:// or mtasa:// link"); + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled()) + return false; + + if (discord->IsDiscordCustomDetailsDisallowed()) + return false; + + if (!discord->SetPresenceButtons(iIndex, szName.c_str(), szUrl.c_str())) + return false; + + return true; +} + +bool CLuaDiscordDefs::IsDiscordRPCConnected() +{ + auto discord = g_pCore->GetDiscord(); + + if (!discord) + return false; + + return discord->IsDiscordRPCEnabled(); +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h new file mode 100644 index 0000000000..fb76298eec --- /dev/null +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once +#include "CLuaDefs.h" + +class CLuaDiscordDefs : public CLuaDefs +{ +public: + static void LoadFunctions(); + static void AddClass(lua_State* luaVM); + +private: + static bool ResetData(); + static bool SetState(std::string strState); + static bool SetAppID(std::string strAppID); + static bool SetDetails(std::string strDetails); + static bool SetAsset(std::string szAsset, std::string szAssetText, bool bIsLarge = false); + static bool SetButtons(unsigned short int iIndex, std::string szName, std::string szUrl); + static bool SetLargeAsset(std::string szAsset, std::string szAssetText); + static bool SetSmallAsset(std::string szAsset, std::string szAssetText); + static bool IsDiscordRPCConnected(); + +}; + diff --git a/Client/sdk/core/CCoreInterface.h b/Client/sdk/core/CCoreInterface.h index cf90751f7f..fe40988f94 100644 --- a/Client/sdk/core/CCoreInterface.h +++ b/Client/sdk/core/CCoreInterface.h @@ -23,6 +23,7 @@ #include "CWebCoreInterface.h" #include "CTrayIconInterface.h" #include "CChatInterface.h" +#include "CDiscordInterface.h" #include "xml/CXML.h" #include @@ -60,21 +61,22 @@ class CCoreInterface // correct MTA version before trying to use any other interface funcs. virtual eCoreVersion GetVersion() = 0; - virtual CConsoleInterface* GetConsole() = 0; - virtual CCommandsInterface* GetCommands() = 0; - virtual CGame* GetGame() = 0; - virtual CGraphicsInterface* GetGraphics() = 0; - virtual CGUI* GetGUI() = 0; - virtual CModManagerInterface* GetModManager() = 0; - virtual CMultiplayer* GetMultiplayer() = 0; - virtual CNet* GetNetwork() = 0; - virtual CXML* GetXML() = 0; - virtual CKeyBindsInterface* GetKeyBinds() = 0; - virtual CXMLNode* GetConfig() = 0; - virtual CCVarsInterface* GetCVars() = 0; - virtual CLocalizationInterface* GetLocalization() = 0; - virtual CWebCoreInterface* GetWebCore() = 0; - virtual CTrayIconInterface* GetTrayIcon() = 0; + virtual CConsoleInterface* GetConsole() = 0; + virtual CCommandsInterface* GetCommands() = 0; + virtual CGame* GetGame() = 0; + virtual CGraphicsInterface* GetGraphics() = 0; + virtual CGUI* GetGUI() = 0; + virtual CModManagerInterface* GetModManager() = 0; + virtual CMultiplayer* GetMultiplayer() = 0; + virtual CNet* GetNetwork() = 0; + virtual CXML* GetXML() = 0; + virtual CKeyBindsInterface* GetKeyBinds() = 0; + virtual CXMLNode* GetConfig() = 0; + virtual CCVarsInterface* GetCVars() = 0; + virtual CLocalizationInterface* GetLocalization() = 0; + virtual CWebCoreInterface* GetWebCore() = 0; + virtual CTrayIconInterface* GetTrayIcon() = 0; + virtual std::shared_ptr GetDiscord() = 0; // Temporary functions for r1 virtual void DebugEcho(const char* szText) = 0; diff --git a/Client/sdk/core/CDiscordInterface.h b/Client/sdk/core/CDiscordInterface.h new file mode 100644 index 0000000000..8b3394fe30 --- /dev/null +++ b/Client/sdk/core/CDiscordInterface.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * (Shared logic for modifications) + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/core/CDiscordInterface.h + * PURPOSE: Discord interface class + * + *****************************************************************************/ + +#pragma once + +class CDiscordInterface +{ +public: + virtual ~CDiscordInterface() = default; + virtual void UpdatePresence() = 0; + virtual bool SetPresenceDetails(const char* szDetails, bool bCustom = false) = 0; + virtual bool SetApplicationID(const char* szAppID) = 0; + virtual bool ResetDiscordData() = 0; + virtual bool SetPresenceState(const char* szState, bool bCustom) = 0; + virtual void SetAssetLargeData(const char* szAsset, const char* szAssetText) = 0; + virtual void SetAssetSmallData(const char* szAsset, const char* szAssetText) = 0; + virtual void SetPresenceStartTimestamp(const unsigned long ulStart) = 0; + virtual bool SetPresenceButtons(unsigned short int iIndex, const char* szName, const char* szUrl) = 0; + //virtual void SetPresenceEndTimestamp(const unsigned long ulEnd) = 0; + + virtual bool SetDiscordRPCEnabled(bool bEnabled = false) = 0; + virtual bool IsDiscordRPCEnabled() const = 0; + virtual bool IsDiscordCustomDetailsDisallowed() const = 0; +}; diff --git a/premake5.lua b/premake5.lua index 44a19ef444..7a97f61d1d 100644 --- a/premake5.lua +++ b/premake5.lua @@ -5,6 +5,7 @@ require "install_data" require "install_resources" require "install_cef" require "install_unifont" +require "install_discord" -- Set CI Build global local ci = os.getenv("CI") @@ -153,6 +154,7 @@ workspace "MTASA" group "Vendor" include "vendor/portaudio" include "vendor/cef3" + include "vendor/discord-rpc" include "vendor/freetype" include "vendor/jpeg-9e" include "vendor/ksignals" diff --git a/utils/buildactions/install_discord.lua b/utils/buildactions/install_discord.lua new file mode 100644 index 0000000000..912ae100b3 --- /dev/null +++ b/utils/buildactions/install_discord.lua @@ -0,0 +1,250 @@ +require 'utils' + +premake.modules.install_discord = {} + +-- Config variables +local DISCORD_PATH = "vendor/discord-rpc/discord/" +local DISCORD_TEMP = "vendor/discord-rpc/discord-rpc.zip" +local DISCORD_UPDATE = "https://api.github.com/repos/multitheftauto/discord-rpc/releases/latest" +local DISCORD_URL = "https://github.com/multitheftauto/discord-rpc/archive/refs/tags/" +local DISCORD_EXT = ".zip" +local RAPID_PATH = "vendor/discord-rpc/discord/thirdparty/rapidjson/" +local RAPID_TEMP = "vendor/discord-rpc/rapidjson.zip" +local RAPID_UPDATE = "https://api.github.com/repos/multitheftauto/rapidjson/releases/latest" +local RAPID_URL = "https://github.com/multitheftauto/rapidjson/archive/refs/tags/" +local RAPID_EXT = ".zip" + +-- Auto-update variables +local DISCORD_VERSION = "v3.4.3" +local DISCORD_HASH = "dacfcf9ac6f005923eef55b4e41f0e46dc64f7a25da88e00faeef86e8553f32f" +local RAPID_VERSION = "v1.1.1" +local RAPID_HASH = "3d638ad2549645a4831e8e98cf7b919f191de0d60816e63e1d5aa08cd56e6db8" + +function make_download_url(url, version, ext) + return url..http.escapeUrlParam(version)..ext +end + +function update_install_discord(variable, version, hash) + local filename = "utils/buildactions/install_discord.lua" + local f = io.open(filename) + local text = f:read("*all") + f:close() + + -- Replace version and hash lines + local version_line = 'local '.. variable ..'_VERSION = "' .. version .. '"' + local hash_line = 'local '.. variable ..'_HASH = "' .. hash .. '"' + text = text:gsub('local '.. variable ..'_VERSION = ".-"', version_line, 1) + text = text:gsub('local '.. variable ..'_HASH = ".-"', hash_line, 1) + + local f = io.open(filename, "w") + f:write(text) + f:close() +end + +local function check_github_update(name, url, version) + print("Checking github for ".. name .. " update...") + local resource, result_str, result_code = http.get(url) + if result_str ~= "OK" or result_code ~= 200 then + errormsg(("Could not get page with status code %s: "):format(response_code), result_str) + os.exit(1) + return + end + + local meta, err = json.decode(resource) + if err then + errormsg("Could not parse json meta data:", err) + os.exit(1) + return + end + + if meta["tag_name"] == version then + print((name .. " is already up to date (%s)"):format(meta["tag_name"])) + return false + end + + io.write(("Does version '%s' look OK to you? (Y/n) "):format(meta["tag_name"])) + local input = io.read():lower() + if not (input == "y" or input == "yes") then + errormsg("Aborting due to user request.") + return false + end + return meta["tag_name"] +end + +local function check_discord(should_upgrade) + local has_discord_dir = os.isdir(DISCORD_PATH) + + -- Check file hash + local archive_path = DISCORD_TEMP + local hash_passed = os.isfile(archive_path) and os.sha256_file(archive_path) == DISCORD_HASH + if hash_passed then + print("discord-rpc consistency checks succeeded") + + if has_discord_dir then + return + end + else + -- Download discord-rpc + print("Downloading discord-rpc " .. DISCORD_VERSION .. "...") + if not http.download_print_errors(make_download_url(DISCORD_URL, DISCORD_VERSION, DISCORD_EXT), archive_path) then + os.exit(1) + return + end + end + + local downloaded_hash = os.sha256_file(archive_path) + if should_upgrade then + print("New discord-rpc hash is:", downloaded_hash) + DISCORD_HASH = downloaded_hash + + io.write(("Update `install_discord.lua` file? (Y/n) "):format(version)) + local input = io.read():lower() + if (input == "y" or input == "yes") then + update_install_discord("DISCORD", DISCORD_VERSION, downloaded_hash) + end + end + + if downloaded_hash == DISCORD_HASH then + print("discord-rpc consistency checks succeeded") + else + errormsg("discord-rpc consistency checks failed.", ("Expected %s, got %s"):format(DISCORD_HASH, downloaded_hash)) + os.exit(1) + return + end + + -- Seriously abort now if we're not using Windows + if os.host() ~= "windows" then + return + end + + -- Delete old discord-rpc files + if has_discord_dir then + if not os.rmdir(DISCORD_PATH) then + errormsg("ERROR: Could not delete discord folder") + os.exit(1) + return + end + end + + if not os.mkdir(DISCORD_PATH) then + errormsg("ERROR: Could not create discord folder (2)") + os.exit(1) + return + end + + -- Extract zip + if not os.extract_archive(archive_path, DISCORD_PATH, true) then + errormsg("ERROR: Could not extract .zip") + os.exit(1) + return + end + + -- Move all files from discord-rpc*/* to ./ + os.expanddir_wildcard(DISCORD_PATH.."discord-rpc*", DISCORD_PATH) +end + +local function check_rapid(should_upgrade) + local has_rapid_dir = os.isdir(RAPID_PATH) + + -- Check file hash + local archive_path = RAPID_TEMP + local hash_passed = os.isfile(archive_path) and os.sha256_file(archive_path) == RAPID_HASH + if hash_passed then + print("rapidjson consistency checks succeeded") + + if has_rapid_dir then + return + end + else + -- Download rapidjson + print("Downloading rapidjson " .. RAPID_VERSION .. "...") + if not http.download_print_errors(make_download_url(RAPID_URL, RAPID_VERSION, RAPID_EXT), archive_path) then + os.exit(1) + return + end + end + + local downloaded_hash = os.sha256_file(archive_path) + if should_upgrade then + print("New rapidjson hash is:", downloaded_hash) + RAPID_HASH = downloaded_hash + + io.write(("Update `install_discord.lua` file? (Y/n) "):format(version)) + local input = io.read():lower() + if (input == "y" or input == "yes") then + update_install_discord("RAPID", RAPID_VERSION, downloaded_hash) + end + end + + if downloaded_hash == RAPID_HASH then + print("rapidjson consistency checks succeeded") + else + errormsg("rapidjson consistency checks failed.", ("Expected %s, got %s"):format(RAPID_HASH, downloaded_hash)) + os.exit(1) + return + end + + -- Seriously abort now if we're not using Windows + if os.host() ~= "windows" then + return + end + + -- Delete old rapidjson files + if has_discord_dir then + if not os.rmdir(RAPID_PATH) then + errormsg("ERROR: Could not delete rapidjson folder") + os.exit(1) + return + end + end + + if not os.mkdir(RAPID_PATH) then + errormsg("ERROR: Could not create rapidjson folder (2)") + os.exit(1) + return + end + + -- Extract zip + if not os.extract_archive(archive_path, RAPID_PATH, true) then + errormsg("ERROR: Could not extract .zip") + os.exit(1) + return + end + + -- Move all files from rapidjson*/* to ./ + os.expanddir_wildcard(RAPID_PATH.."rapidjson*", RAPID_PATH) +end + +newaction { + trigger = "install_discord", + description = "Downloads and installs discord-rpc", + + execute = function(...) + local should_upgrade = _ARGS[1] == "upgrade" + if should_upgrade then + -- discord-rpc + local discord = check_github_update("discord-rpc", DISCORD_UPDATE, DISCORD_VERSION) + if discord then + DISCORD_VERSION = discord + DISCORD_HASH = "" + end + + -- rapidjson + local rapidjson = check_github_update("rapidjson", RAPID_UPDATE, RAPID_VERSION) + if rapidjson then + RAPID_VERSION = rapidjson + RAPID_HASH = "" + end + end + + -- Only execute on Windows in normal scenarios + if os.host() ~= "windows" and not should_upgrade then + return + end + + check_discord(should_upgrade) + check_rapid(should_upgrade) + end +} + +return premake.modules.install_discord \ No newline at end of file diff --git a/vendor/discord-rpc/.gitignore b/vendor/discord-rpc/.gitignore new file mode 100644 index 0000000000..26e682d42b --- /dev/null +++ b/vendor/discord-rpc/.gitignore @@ -0,0 +1,3 @@ +*.zip + +discord/ \ No newline at end of file diff --git a/vendor/discord-rpc/premake5.lua b/vendor/discord-rpc/premake5.lua new file mode 100644 index 0000000000..dbe7e5776c --- /dev/null +++ b/vendor/discord-rpc/premake5.lua @@ -0,0 +1,23 @@ +project "discord-rpc" + targetname "discord-rpc" + language "C++" + kind "StaticLib" + + includedirs { + "discord/include", + "discord/thirdparty/rapidjson/include" + } + + files { + "premake5.lua", + "discord/src/discord_rpc.cpp", + "discord/src/rpc_connection.cpp", + "discord/src/serialization.cpp", + "discord/src/connection_win.cpp", + "discord/src/discord_register_win.cpp", + } + + filter "architecture:not x86" + flags { "ExcludeFromBuild" } + filter "system:not windows" + flags { "ExcludeFromBuild" } \ No newline at end of file diff --git a/win-create-projects.bat b/win-create-projects.bat index 4908729981..cabad31c5d 100644 --- a/win-create-projects.bat +++ b/win-create-projects.bat @@ -6,6 +6,9 @@ utils\premake5.exe install_cef rem Update Unifont utils\premake5.exe install_unifont +rem Update discord-rpc +utils\premake5.exe install_discord + rem Generate solutions utils\premake5.exe vs2022