From ef26810df4542283fee8edcc165bc9be22f2ca98 Mon Sep 17 00:00:00 2001 From: TEDERIs Date: Tue, 24 Oct 2023 09:28:12 +0700 Subject: [PATCH] Discord RPC (Discord Rich Presence) tweaks (#3216) The changelog of this can be found in #3205 (as #3216 is the successor of #3205 and a near-copy). It was done this way because the #3205's author is on vacation, and we had pending code review changes to make. --- Client/core/CCore.h | 126 ++++++------ Client/core/CDiscordRichPresence.cpp | 39 +++- Client/core/CDiscordRichPresence.h | 6 + Client/core/CSettings.cpp | 20 ++ Client/mods/deathmatch/logic/CClientGame.cpp | 182 ++++++++++++++---- Client/mods/deathmatch/logic/CClientGame.h | 37 ++-- Client/mods/deathmatch/logic/CClientTask.h | 31 +++ Client/mods/deathmatch/logic/CClientVehicle.h | 8 +- .../mods/deathmatch/logic/CPacketHandler.cpp | 18 ++ .../logic/luadefs/CLuaDiscordDefs.cpp | 87 +++++++-- .../logic/luadefs/CLuaDiscordDefs.h | 3 + Client/sdk/core/CCoreInterface.h | 53 ++--- Client/sdk/core/CDiscordInterface.h | 2 + Client/sdk/game/CTaskManager.h | 2 +- Server/mods/deathmatch/logic/CGame.cpp | 2 +- .../packets/CPlayerJoinCompletePacket.cpp | 7 +- .../logic/packets/CPlayerJoinCompletePacket.h | 3 +- Shared/sdk/net/bitstream.h | 4 + 18 files changed, 470 insertions(+), 160 deletions(-) diff --git a/Client/core/CCore.h b/Client/core/CCore.h index 5d7639a0f6..4e706e94b7 100644 --- a/Client/core/CCore.h +++ b/Client/core/CCore.h @@ -45,30 +45,30 @@ class CDiscordInterface; #define DIRECTINPUT_VERSION 0x0800 #include -#define BLUE_VERSION_STRING "Multi Theft Auto v" MTA_DM_BUILDTAG_LONG -#define BLUE_COPYRIGHT_STRING "Copyright (C) 2003 - %BUILD_YEAR% Multi Theft Auto" +#define BLUE_VERSION_STRING "Multi Theft Auto v" MTA_DM_BUILDTAG_LONG +#define BLUE_COPYRIGHT_STRING "Copyright (C) 2003 - %BUILD_YEAR% Multi Theft Auto" // Configuration file path (relative to MTA install directory) -#define MTA_CONFIG_PATH "mta/config/coreconfig.xml" -#define MTA_SERVER_CACHE_PATH "mta/config/servercache.xml" -#define MTA_CONSOLE_LOG_PATH "mta/logs/console.log" -#define MTA_CONSOLE_INPUT_LOG_PATH "mta/logs/console-input.log" -#define CONFIG_ROOT "mainconfig" -#define CONFIG_NODE_CVARS "settings" // cvars node -#define CONFIG_NODE_KEYBINDS "binds" // keybinds node -#define CONFIG_NODE_JOYPAD "joypad" -#define CONFIG_NODE_UPDATER "updater" -#define CONFIG_NODE_SERVER_INT "internet_servers" // backup of last successful master server list query -#define CONFIG_NODE_SERVER_FAV "favourite_servers" // favourite servers list node -#define CONFIG_NODE_SERVER_REC "recently_played_servers" // recently played servers list node -#define CONFIG_NODE_SERVER_OPTIONS "serverbrowser_options" // saved options for the server browser -#define CONFIG_NODE_SERVER_SAVED "server_passwords" // This contains saved passwords (as appose to save_server_passwords which is a setting) -#define CONFIG_NODE_SERVER_HISTORY "connect_history" -#define CONFIG_INTERNET_LIST_TAG "internet_server" -#define CONFIG_FAVOURITE_LIST_TAG "favourite_server" -#define CONFIG_RECENT_LIST_TAG "recently_played_server" -#define CONFIG_HISTORY_LIST_TAG "connected_server" -#define IDT_TIMER1 1234 +#define MTA_CONFIG_PATH "mta/config/coreconfig.xml" +#define MTA_SERVER_CACHE_PATH "mta/config/servercache.xml" +#define MTA_CONSOLE_LOG_PATH "mta/logs/console.log" +#define MTA_CONSOLE_INPUT_LOG_PATH "mta/logs/console-input.log" +#define CONFIG_ROOT "mainconfig" +#define CONFIG_NODE_CVARS "settings" // cvars node +#define CONFIG_NODE_KEYBINDS "binds" // keybinds node +#define CONFIG_NODE_JOYPAD "joypad" +#define CONFIG_NODE_UPDATER "updater" +#define CONFIG_NODE_SERVER_INT "internet_servers" // backup of last successful master server list query +#define CONFIG_NODE_SERVER_FAV "favourite_servers" // favourite servers list node +#define CONFIG_NODE_SERVER_REC "recently_played_servers" // recently played servers list node +#define CONFIG_NODE_SERVER_OPTIONS "serverbrowser_options" // saved options for the server browser +#define CONFIG_NODE_SERVER_SAVED "server_passwords" // This contains saved passwords (as appose to save_server_passwords which is a setting) +#define CONFIG_NODE_SERVER_HISTORY "connect_history" +#define CONFIG_INTERNET_LIST_TAG "internet_server" +#define CONFIG_FAVOURITE_LIST_TAG "favourite_server" +#define CONFIG_RECENT_LIST_TAG "recently_played_server" +#define CONFIG_HISTORY_LIST_TAG "connected_server" +#define IDT_TIMER1 1234 extern class CCore* g_pCore; extern class CGraphics* g_pGraphics; @@ -84,26 +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; }; - std::shared_ptr GetDiscord(); + 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); @@ -285,6 +285,10 @@ class CCore : public CCoreInterface, public CSingleton void SetCustomStreamingMemory(size_t szMB); bool IsUsingCustomStreamingMemorySize(); size_t GetStreamingMemory(); + + const SString& GetLastConnectedServerName() const { return m_strLastConnectedServerName; } + void SetLastConnectedServerName(const SString& strServerName) { m_strLastConnectedServerName = strServerName; } + private: void ApplyCoreInitSettings(); @@ -299,11 +303,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; - std::shared_ptr m_pDiscordRichPresence; + CXMLFile* m_pConfigFile; + CClientVariables m_ClientVariables; + CWebCoreInterface* m_pWebCore = nullptr; + CTrayIcon* m_pTrayIcon; + std::shared_ptr m_pDiscordRichPresence; // Hook interfaces. CMessageLoopHook* m_pMessageLoopHook; @@ -369,22 +373,24 @@ class CCore : public CCoreInterface, public CSingleton EDiagnosticDebugType m_DiagnosticDebug; // Below 2 are used for the UI only - float m_fMinStreamingMemory{}; - float m_fMaxStreamingMemory{}; + float m_fMinStreamingMemory{}; + float m_fMaxStreamingMemory{}; // Custom streaming memory limit set by `engineStreamingSetMemorySize` - Reset on server connects (= set to 0), or by the scripter // `0` means "not set" [so the value should be ignored] - size_t m_CustomStreamingMemoryLimitBytes{}; - - bool m_bGettingIdleCallsFromMultiplayer; - bool m_bWindowsTimerEnabled; - bool m_bModulesLoaded; - int m_iDummyProgressValue; - HANDLE m_DummyProgressTimerHandle; - SString m_strDummyProgressType; - bool m_bDummyProgressUpdateAlways; - bool m_bIsRenderingGrass; - bool m_bFakeLagCommandEnabled; + size_t m_CustomStreamingMemoryLimitBytes{}; + + bool m_bGettingIdleCallsFromMultiplayer; + bool m_bWindowsTimerEnabled; + bool m_bModulesLoaded; + int m_iDummyProgressValue; + HANDLE m_DummyProgressTimerHandle; + SString m_strDummyProgressType; + bool m_bDummyProgressUpdateAlways; + bool m_bIsRenderingGrass; + bool m_bFakeLagCommandEnabled; + + SString m_strLastConnectedServerName{}; // Command line static void ParseCommandLine(std::map& options, const char*& szArgs, const char** pszNoValOptions = NULL); diff --git a/Client/core/CDiscordRichPresence.cpp b/Client/core/CDiscordRichPresence.cpp index 686fa89d21..1edab1dd8c 100644 --- a/Client/core/CDiscordRichPresence.cpp +++ b/Client/core/CDiscordRichPresence.cpp @@ -64,12 +64,15 @@ void CDiscordRichPresence::SetDefaultData() m_strDiscordAppAssetSmallText = DEFAULT_APP_ASSET_SMALL_TEXT; m_strDiscordAppCurrentId = DEFAULT_APP_ID; - m_strDiscordAppDetails.clear(); + m_strDiscordAppCustomDetails.clear(); m_strDiscordAppCustomState.clear(); m_aButtons = {}; m_bUpdateRichPresence = true; m_bDisallowCustomDetails = true; + + m_uiDiscordAppStart = 0; + m_uiDiscordAppEnd = 0; } void CDiscordRichPresence::UpdatePresence() @@ -87,8 +90,10 @@ void CDiscordRichPresence::UpdatePresence() discordPresence.state = (!m_strDiscordAppCustomState.empty() || !m_bDisallowCustomDetails) ? m_strDiscordAppCustomState.c_str() : m_strDiscordAppState.c_str(); - discordPresence.details = m_strDiscordAppDetails.c_str(); + discordPresence.details = + (!m_strDiscordAppCustomDetails.empty() || !m_bDisallowCustomDetails) ? m_strDiscordAppCustomDetails.c_str() : m_strDiscordAppDetails.c_str(); discordPresence.startTimestamp = m_uiDiscordAppStart; + discordPresence.endTimestamp = m_uiDiscordAppEnd; DiscordButton buttons[2]; if (m_aButtons) @@ -101,6 +106,9 @@ void CDiscordRichPresence::UpdatePresence() discordPresence.buttons = buttons; } + discordPresence.partySize = (m_bDisallowCustomDetails) ? 0 : m_iPartySize; + discordPresence.partyMax = (m_bDisallowCustomDetails) ? 0 : m_iPartyMax; + Discord_UpdatePresence(&discordPresence); m_bUpdateRichPresence = false; } @@ -111,6 +119,13 @@ void CDiscordRichPresence::SetPresenceStartTimestamp(const unsigned long ulStart m_bUpdateRichPresence = true; } +void CDiscordRichPresence::SetPresenceEndTimestamp(const unsigned long ulEnd) +{ + m_uiDiscordAppEnd = ulEnd; + m_bUpdateRichPresence = true; +} + + void CDiscordRichPresence::SetAssetLargeData(const char* szAsset, const char* szAssetText) { SetAsset(szAsset, szAssetText, true); @@ -125,13 +140,13 @@ void CDiscordRichPresence::SetAsset(const char* szAsset, const char* szAssetText { if (isLarge) { - m_strDiscordAppAsset = (szAsset && *szAsset) ? szAsset : DEFAULT_APP_ASSET; - m_strDiscordAppAssetText = (szAssetText && *szAssetText) ? szAssetText : DEFAULT_APP_ASSET_TEXT; + m_strDiscordAppAsset = (std::strlen(szAsset) > 0 && szAsset && *szAsset) ? szAsset : DEFAULT_APP_ASSET; + m_strDiscordAppAssetText = (std::strlen(szAssetText) > 0 && 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_strDiscordAppAssetSmall = (std::strlen(szAsset) > 0 && szAsset && *szAsset) ? szAsset : DEFAULT_APP_ASSET_SMALL; + m_strDiscordAppAssetSmallText = (std::strlen(szAssetText) > 0 && szAssetText && *szAssetText) ? szAssetText : DEFAULT_APP_ASSET_SMALL_TEXT; } m_bUpdateRichPresence = true; } @@ -172,7 +187,11 @@ bool CDiscordRichPresence::SetPresenceButtons(unsigned short int iIndex, const c bool CDiscordRichPresence::SetPresenceDetails(const char* szDetails, bool bCustom) { - m_strDiscordAppDetails = szDetails; + if (bCustom) + m_strDiscordAppCustomDetails = szDetails; + else + m_strDiscordAppDetails = szDetails; + m_bUpdateRichPresence = true; return true; } @@ -225,3 +244,9 @@ bool CDiscordRichPresence::IsDiscordCustomDetailsDisallowed() const { return m_bDisallowCustomDetails; } + +void CDiscordRichPresence::SetPresencePartySize(int iSize, int iMax) +{ + m_iPartySize = iSize; + m_iPartyMax = iMax; +} diff --git a/Client/core/CDiscordRichPresence.h b/Client/core/CDiscordRichPresence.h index 5301b12959..3108c78d9e 100644 --- a/Client/core/CDiscordRichPresence.h +++ b/Client/core/CDiscordRichPresence.h @@ -26,6 +26,7 @@ class CDiscordRichPresence : public CDiscordInterface void UpdatePresence(); void SetPresenceStartTimestamp(const unsigned long ulStart); + void SetPresenceEndTimestamp(const unsigned long ulEnd); 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); @@ -34,6 +35,7 @@ class CDiscordRichPresence : public CDiscordInterface 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); + void SetPresencePartySize(int iSize, int iMax); bool SetDiscordRPCEnabled(bool bEnabled); bool IsDiscordCustomDetailsDisallowed() const; bool IsDiscordRPCEnabled() const; @@ -54,6 +56,7 @@ class CDiscordRichPresence : public CDiscordInterface std::string m_strDiscordAppState; std::string m_strDiscordAppDetails; std::string m_strDiscordAppCustomState; + std::string m_strDiscordAppCustomDetails; std::optional, std::pair>> m_aButtons; @@ -63,4 +66,7 @@ class CDiscordRichPresence : public CDiscordInterface bool m_bDisallowCustomDetails; bool m_bDiscordRPCEnabled; bool m_bUpdateRichPresence; + + int m_iPartySize; + int m_iPartyMax; }; diff --git a/Client/core/CSettings.cpp b/Client/core/CSettings.cpp index 369b67c4aa..d758b562c1 100644 --- a/Client/core/CSettings.cpp +++ b/Client/core/CSettings.cpp @@ -3452,6 +3452,26 @@ void CSettings::SaveData() CVARS_SET("allow_discord_rpc", bAllowDiscordRPC); g_pCore->GetDiscord()->SetDiscordRPCEnabled(bAllowDiscordRPC); + if (bAllowDiscordRPC) + { + auto discord = g_pCore->GetDiscord(); + + if (discord) + { + const char* state = "Main menu"; + + if (g_pCore->IsConnected()) + { + state = "In-game"; + + const SString& serverName = g_pCore->GetLastConnectedServerName(); + discord->SetPresenceDetails(serverName.c_str(), false); + } + + discord->SetPresenceState(state, false); + } + } + // Grass bool bGrassEnabled = m_pCheckBoxGrass->GetSelected(); CVARS_SET("grass", bGrassEnabled); diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index c94ca60e6d..9f71a921b2 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -48,7 +48,7 @@ using std::list; using std::vector; // Hide the "conversion from 'unsigned long' to 'DWORD*' of greater size" warning -#pragma warning(disable:4312) +#pragma warning(disable : 4312) // Used within this file by the packet handler to grab the this pointer of CClientGame extern CClientGame* g_pClientGame; @@ -69,6 +69,8 @@ CVector g_vecBulletFireEndPosition; #define DOUBLECLICK_TIMEOUT 330 #define DOUBLECLICK_MOVE_THRESHOLD 10.0f +static constexpr long long TIME_DISCORD_UPDATE_RATE = 15000; + CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) { // Init the global var with ourself @@ -106,6 +108,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_fGameSpeed = 1.0f; m_lMoney = 0; m_dwWanted = 0; + m_timeLastDiscordStateUpdate = 0; m_lastWeaponSlot = WEAPONSLOT_MAX; // last stored weapon slot, for weapon slot syncing to server (sets to invalid value) ResetAmmoInClip(); @@ -330,17 +333,17 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) m_bBeingDeleted = false; - #if defined (MTA_DEBUG) || defined (MTA_BETA) +#if defined(MTA_DEBUG) || defined(MTA_BETA) m_bShowSyncingInfo = false; - #endif +#endif - #ifdef MTA_DEBUG +#ifdef MTA_DEBUG m_pShowPlayer = m_pShowPlayerTasks = NULL; m_bMimicLag = false; m_ulLastMimicLag = 0; m_bDoPaintballs = false; m_bShowInterpolation = false; - #endif +#endif // Add our lua events AddBuiltInEvents(); @@ -394,8 +397,8 @@ CClientGame::~CClientGame() // Reset CGUI's global events g_pCore->GetGUI()->ClearInputHandlers(INPUT_MOD); - // Destroy mimics - #ifdef MTA_DEBUG +// Destroy mimics +#ifdef MTA_DEBUG list::const_iterator iterMimics = m_Mimics.begin(); for (; iterMimics != m_Mimics.end(); iterMimics++) { @@ -406,7 +409,7 @@ CClientGame::~CClientGame() delete pPlayer; } - #endif +#endif // Hide the transfer box incase it is showing m_pTransferBox->Hide(); @@ -492,6 +495,7 @@ CClientGame::~CClientGame() if (discord && discord->IsDiscordRPCEnabled()) { discord->ResetDiscordData(); + discord->SetPresenceState("Main menu", false); discord->UpdatePresence(); } @@ -799,7 +803,7 @@ void CClientGame::DoPulsePreHUDRender(bool bDidUnminimize, bool bDidRecreateRend void CClientGame::DoPulsePostFrame() { TIMING_CHECKPOINT("+CClientGame::DoPulsePostFrame"); - #ifdef DEBUG_KEYSTATES +#ifdef DEBUG_KEYSTATES // Get the controller state CControllerState cs; g_pGame->GetPad()->GetCurrentControllerState(&cs); @@ -837,7 +841,7 @@ void CClientGame::DoPulsePostFrame() cs.m_bVehicleMouseLook, cs.LeftStickX, cs.LeftStickY, cs.RightStickX, cs.RightStickY); g_pCore->GetGraphics()->DrawTextTTF(300, 320, 1280, 800, 0xFFFFFFFF, strBuffer, 1.0f, 0); - #endif +#endif UpdateModuleTickCount64(); @@ -895,7 +899,8 @@ void CClientGame::DoPulsePostFrame() } // Adjust the streaming memory size cvar [if needed] - if (!g_pCore->IsUsingCustomStreamingMemorySize()) { + if (!g_pCore->IsUsingCustomStreamingMemorySize()) + { unsigned int uiStreamingMemoryPrev; g_pCore->GetCVars()->Get("streaming_memory", uiStreamingMemoryPrev); uint uiStreamingMemory = SharedUtil::Clamp(g_pCore->GetMinStreamingMemory(), uiStreamingMemoryPrev, g_pCore->GetMaxStreamingMemory()); @@ -904,12 +909,13 @@ void CClientGame::DoPulsePostFrame() } const auto streamingMemorySizeBytes = g_pCore->GetStreamingMemory(); - if (g_pMultiplayer->GetLimits()->GetStreamingMemory() != streamingMemorySizeBytes) { + if (g_pMultiplayer->GetLimits()->GetStreamingMemory() != streamingMemorySizeBytes) + { g_pMultiplayer->GetLimits()->SetStreamingMemory(streamingMemorySizeBytes); } - // If we're in debug mode and are supposed to show task data, do it - #ifdef MTA_DEBUG +// If we're in debug mode and are supposed to show task data, do it +#ifdef MTA_DEBUG if (m_pShowPlayerTasks) { DrawTasks(m_pShowPlayerTasks); @@ -927,9 +933,9 @@ void CClientGame::DoPulsePostFrame() if (pPlayer->IsStreamedIn() && pPlayer->IsShowingWepdata()) DrawWeaponsyncData(pPlayer); } - #endif +#endif - #if defined (MTA_DEBUG) || defined (MTA_BETA) +#if defined(MTA_DEBUG) || defined(MTA_BETA) if (m_bShowSyncingInfo) { // Draw the header boxz @@ -949,7 +955,7 @@ void CClientGame::DoPulsePostFrame() m_pDisplayManager->DrawText2D(strBuffer, vecPosition, 1.0f, 0xFFFFFFFF); } } - #endif +#endif // Heli Clear time if (m_LastClearTime.Get() > HeliKill_List_Clear_Rate) { @@ -958,6 +964,100 @@ void CClientGame::DoPulsePostFrame() m_LastClearTime.Reset(); } + // Check if we need to update the Discord Rich Presence state + if (const long long ticks = GetTickCount64_(); ticks > m_timeLastDiscordStateUpdate + TIME_DISCORD_UPDATE_RATE) + { + auto discord = g_pCore->GetDiscord(); + + if (discord && discord->IsDiscordRPCEnabled()) + { + if (auto pLocalPlayer = g_pClientGame->GetLocalPlayer()) + { + CVector position; + SString zoneName; + + pLocalPlayer->GetPosition(position); + CStaticFunctionDefinitions::GetZoneName(position, zoneName, true); + + if (zoneName == "Unknown") + { + zoneName = "Area 51"; + } + + auto taskManager = pLocalPlayer->GetTaskManager(); + auto task = taskManager->GetActiveTask(); + auto pVehicle = pLocalPlayer->GetOccupiedVehicle(); + bool useZoneName = true; + + const eClientVehicleType vehicleType = (pVehicle) ? CClientVehicleManager::GetVehicleType(pVehicle->GetModel()) : CLIENTVEHICLE_NONE; + std::string discordState = (pVehicle) ? g_vehicleTypePrefixes.at(vehicleType).c_str() : "Walking around "; + + if (task && task->IsValid()) + { + const auto taskSub = task->GetSubTask(); + const auto taskType = task->GetTaskType(); + + // Check for states which match our primary task + std::vector taskStates; + for (const auto& [task, state] : g_playerTaskStates) + { + if (task == taskType) + taskStates.push_back(state); + } + + // Check for non-matching sub/secondary tasks and remove them + for (auto it = taskStates.begin(); it != taskStates.end(); ) + { + const STaskState& taskState = (*it); + + const auto taskSecondary = + (!taskState.eSecondaryType.has_value()) ? nullptr : taskManager->GetTaskSecondary(taskState.eSecondaryType.value()); + bool useState = (!taskState.eSubTask.has_value() && !taskState.eSecondaryTask.has_value()); + + if (!useState) + { + if (taskSub != nullptr && taskState.eSubTask.has_value() && taskState.eSubTask.value() == taskSub->GetTaskType()) + useState = true; + else if (taskSecondary != nullptr && taskState.eSecondaryTask.has_value() && + taskState.eSecondaryTask.value() == taskSecondary->GetTaskType()) + useState = true; + } + + if (!useState) + it = taskStates.erase(it); + else + ++it; + } + + // Choose a random task state (if we have any) + const int stateCount = taskStates.size(); + if (stateCount > 0) + { + std::srand(GetTickCount64_()); + const int index = (std::rand() % stateCount); + const auto& taskState = taskStates[index]; + + discordState = taskState.strState; + useZoneName = taskState.bUseZone; + } + + if (useZoneName) + { + discordState.append(" " + zoneName); + } + + discord->SetPresenceState(discordState.c_str(), false); + } + } + else + { + discord->SetPresenceState("In-game", false); + } + + m_timeLastDiscordStateUpdate = ticks; + } + } + CClientPerfStatManager::GetSingleton()->DoPulse(); } @@ -1079,9 +1179,9 @@ void CClientGame::DoPulses() GetModelCacheManager()->DoPulse(); - #ifdef MTA_DEBUG +#ifdef MTA_DEBUG UpdateMimics(); - #endif +#endif // Grab the current time unsigned long ulCurrentTime = CClientTime::GetTime(); @@ -3370,21 +3470,23 @@ void CClientGame::Event_OnIngameAndConnected() void CClientGame::SetupGlobalLuaEvents() { // Setup onClientPaste event - m_Delegate.connect(g_pCore->GetKeyBinds()->OnPaste, [this](const SString& clipboardText) { - // Don't trigger if main menu or console is open or the cursor is not visible - if (!AreCursorEventsEnabled() || g_pCore->IsMenuVisible() || g_pCore->GetConsole()->IsInputActive()) - return; + m_Delegate.connect(g_pCore->GetKeyBinds()->OnPaste, + [this](const SString& clipboardText) + { + // Don't trigger if main menu or console is open or the cursor is not visible + if (!AreCursorEventsEnabled() || g_pCore->IsMenuVisible() || g_pCore->GetConsole()->IsInputActive()) + return; - // Also don't trigger if remote web browser view is focused - CWebViewInterface* pFocusedBrowser = g_pCore->IsWebCoreLoaded() ? g_pCore->GetWebCore()->GetFocusedWebView() : nullptr; - if (pFocusedBrowser && !pFocusedBrowser->IsLocal()) - return; + // Also don't trigger if remote web browser view is focused + CWebViewInterface* pFocusedBrowser = g_pCore->IsWebCoreLoaded() ? g_pCore->GetWebCore()->GetFocusedWebView() : nullptr; + if (pFocusedBrowser && !pFocusedBrowser->IsLocal()) + return; - // Call event now - CLuaArguments args; - args.PushString(clipboardText); - m_pRootEntity->CallEvent("onClientPaste", args, false); - }); + // Call event now + CLuaArguments args; + args.PushString(clipboardText); + m_pRootEntity->CallEvent("onClientPaste", args, false); + }); } bool CClientGame::StaticBreakTowLinkHandler(CVehicle* pTowingVehicle) @@ -3963,13 +4065,13 @@ bool CClientGame::ProcessCollisionHandler(CEntitySAInterface* pThisInterface, CE if (pEntity && pColEntity) { - #if MTA_DEBUG +#if MTA_DEBUG CClientEntity* ppThisEntity2 = iter1->second; CClientEntity* ppOtherEntity2 = iter2->second; // These should match, but its not essential. assert(ppThisEntity2 == pEntity); assert(ppOtherEntity2 == pColEntity); - #endif +#endif if (!pEntity->IsCollidableWith(pColEntity)) return false; } @@ -5553,6 +5655,16 @@ void CClientGame::DoWastedCheck(ElementID damagerID, unsigned char ucWeapon, uns // Send the packet g_pNet->SendPacket(PACKET_ID_PLAYER_WASTED, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); g_pNet->DeallocateNetBitStream(pBitStream); + + auto discord = g_pCore->GetDiscord(); + if (discord->IsDiscordRPCEnabled()) + { + static const std::vector states{"In a ditch", "En-route to hospital", "Meeting their maker", "Regretting their decisions", + "Wasted"}; + + const std::string& state = states[rand() % states.size()]; + discord->SetPresenceState(state.c_str(), false); + } } } } @@ -6637,7 +6749,7 @@ void CClientGame::RestreamModel(unsigned short usModel) // 'Restream' upgrades after model replacement to propagate visual changes with immediate effect if (CClientObjectManager::IsValidModel(usModel) && CVehicleUpgrades::IsUpgrade(usModel)) - m_pManager->GetVehicleManager()->RestreamVehicleUpgrades(usModel); + m_pManager->GetVehicleManager()->RestreamVehicleUpgrades(usModel); } void CClientGame::RestreamWorld(bool removeBigBuildings) diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 5f9ed80574..96d82c37a7 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -1,3 +1,4 @@ +/***************************************************************************** /***************************************************************************** * * PROJECT: Multi Theft Auto v1.0 @@ -45,8 +46,8 @@ #include "CSingularFileDownloadManager.h" #include "CObjectRespawner.h" -#define HeliKill_List_Clear_Rate 500 -#define MIN_PUSH_ANTISPAM_RATE 1500 +#define HeliKill_List_Clear_Rate 500 +#define MIN_PUSH_ANTISPAM_RATE 1500 #define INVALID_DOWNLOAD_PRIORITY_GROUP (INT_MIN) class CClientModelCacheManager; @@ -315,15 +316,15 @@ class CClientGame void ShowEaeg(bool bShow); void ShowFPS(bool bShow) { m_bShowFPS = bShow; }; - #if defined (MTA_DEBUG) || defined (MTA_BETA) +#if defined(MTA_DEBUG) || defined(MTA_BETA) void ShowSyncingInfo(bool bShow) { m_bShowSyncingInfo = bShow; }; - #endif +#endif #ifdef MTA_WEPSYNCDBG void ShowWepdata(const char* szNick); #endif - #ifdef MTA_DEBUG +#ifdef MTA_DEBUG void ShowWepdata(const char* szNick); void ShowTasks(const char* szNick); void ShowPlayer(const char* szNick); @@ -332,7 +333,7 @@ class CClientGame void SetDoPaintballs(bool bDoPaintballs) { m_bDoPaintballs = bDoPaintballs; } void ShowInterpolation(bool bShow) { m_bShowInterpolation = bShow; } bool IsShowingInterpolation() const { return m_bShowInterpolation; } - #endif +#endif CEntity* GetTargetedGameEntity() { return m_pTargetedGameEntity; } CClientEntity* GetTargetedEntity() { return m_pTargetedEntity; } @@ -476,17 +477,17 @@ class CClientGame void DrawFPS(); - #ifdef MTA_DEBUG +#ifdef MTA_DEBUG void DrawTasks(CClientPlayer* pPlayer); void DrawPlayerDetails(CClientPlayer* pPlayer); void UpdateMimics(); void DoPaintballs(); void DrawWeaponsyncData(CClientPlayer* pPlayer); - #endif +#endif - #ifdef MTA_WEPSYNCDBG +#ifdef MTA_WEPSYNCDBG void DrawWeaponsyncData(CClientPlayer* pPlayer); - #endif +#endif void DownloadSingularResourceFiles(); @@ -791,11 +792,11 @@ class CClientGame std::map m_CachedCollisionMap; bool m_BuiltCollisionMapThisFrame; - #if defined (MTA_DEBUG) || defined (MTA_BETA) +#if defined(MTA_DEBUG) || defined(MTA_BETA) bool m_bShowSyncingInfo; - #endif +#endif - #ifdef MTA_DEBUG +#ifdef MTA_DEBUG CClientPlayer* m_pShowPlayerTasks; CClientPlayer* m_pShowPlayer; std::list m_Mimics; @@ -807,10 +808,10 @@ class CClientGame CVector m_vecLastMimicRot; bool m_bDoPaintballs; bool m_bShowInterpolation; - #endif - bool m_bDevelopmentMode; - bool m_bShowCollision; - bool m_bShowSound; +#endif + bool m_bDevelopmentMode; + bool m_bShowCollision; + bool m_bShowSound; // Debug class. Empty in release. public: @@ -848,6 +849,8 @@ class CClientGame AnimAssociations_type m_mapOfCustomAnimationAssociations; // Key is the task and value is the CClientPed* RunNamedAnimTask_type m_mapOfRunNamedAnimTasks; + + long long m_timeLastDiscordStateUpdate; }; extern CClientGame* g_pClientGame; diff --git a/Client/mods/deathmatch/logic/CClientTask.h b/Client/mods/deathmatch/logic/CClientTask.h index 5e8d48fbe5..540d34431d 100644 --- a/Client/mods/deathmatch/logic/CClientTask.h +++ b/Client/mods/deathmatch/logic/CClientTask.h @@ -13,6 +13,7 @@ #include #include "lua/LuaCommon.h" #include "CClientEntity.h" +#include class CClientEntity; class CClientManager; @@ -23,6 +24,36 @@ class CPed; class CTask; class CVehicle; +struct STaskState +{ + bool bUseZone; + std::string strState; + std::optional eSubTask = {}; + std::optional eSecondaryTask = {}; + std::optional eSecondaryType = {}; +}; + +static const std::unordered_multimap g_playerTaskStates{ + {TASK_COMPLEX_JUMP, {true, "Climbing around in", TASK_SIMPLE_CLIMB}}, + {TASK_SIMPLE_GANG_DRIVEBY, {true, "Doing a drive-by in"}}, + {TASK_SIMPLE_DRIVEBY_SHOOT, {true, "Doing a drive-by in"}}, + {TASK_SIMPLE_DIE, {false, "Blub blub...", TASK_SIMPLE_DROWN}}, + {TASK_SIMPLE_DIE, {false, "Breathing water", TASK_SIMPLE_DROWN}}, + {TASK_SIMPLE_DIE, {true, "Drowning in", TASK_SIMPLE_DROWN}}, + {TASK_SIMPLE_PLAYER_ON_FOOT, {true, "Ducking for cover in", {}, TASK_SIMPLE_DUCK, TASK_SECONDARY_DUCK}}, + {TASK_SIMPLE_PLAYER_ON_FOOT, {true, "Fighting in", {}, TASK_SIMPLE_FIGHT, TASK_SECONDARY_ATTACK}}, + {TASK_SIMPLE_PLAYER_ON_FOOT, {true, "Throwing fists in", {}, TASK_SIMPLE_FIGHT, TASK_SECONDARY_ATTACK}}, + {TASK_SIMPLE_PLAYER_ON_FOOT, {true, "Blastin' fools in", {}, TASK_SIMPLE_USE_GUN, TASK_SECONDARY_ATTACK}}, + {TASK_SIMPLE_PLAYER_ON_FOOT, {true, "Shooting up", {}, TASK_SIMPLE_USE_GUN, TASK_SECONDARY_ATTACK}}, + {TASK_SIMPLE_JETPACK, {true, "Jetpacking in"}}, + {TASK_SIMPLE_PLAYER_ON_FOOT, {true, "Literally on fire in", {}, TASK_SIMPLE_PLAYER_ON_FIRE, TASK_SECONDARY_PARTIAL_ANIM}}, + {TASK_SIMPLE_PLAYER_ON_FOOT, {true, "Burning up in", {}, TASK_SIMPLE_PLAYER_ON_FIRE, TASK_SECONDARY_PARTIAL_ANIM}}, + {TASK_COMPLEX_IN_WATER, {true, "Swimming in", TASK_SIMPLE_SWIM}}, + {TASK_COMPLEX_IN_WATER, {true, "Floating around in", TASK_SIMPLE_SWIM}}, + {TASK_COMPLEX_IN_WATER, {false, "Being chased by a shark", TASK_SIMPLE_SWIM}}, + {TASK_SIMPLE_CHOKING, {true, "Choking to death in"}}, +}; + class CClientTask { public: diff --git a/Client/mods/deathmatch/logic/CClientVehicle.h b/Client/mods/deathmatch/logic/CClientVehicle.h index 3aa52362fa..89f88e607d 100644 --- a/Client/mods/deathmatch/logic/CClientVehicle.h +++ b/Client/mods/deathmatch/logic/CClientVehicle.h @@ -32,7 +32,7 @@ class CClientProjectile; #define INVALID_PASSENGER_SEAT 0xFF #define DEFAULT_VEHICLE_HEALTH 1000 -#define MAX_VEHICLE_HEALTH 10000 +#define MAX_VEHICLE_HEALTH 10000 enum eClientVehicleType { @@ -49,6 +49,8 @@ enum eClientVehicleType CLIENTVEHICLE_TRAILER }; +static constexpr int NUM_VEHICLE_TYPES = 11; + enum eDelayedSyncVehicleData { DELAYEDSYNC_VEHICLE_KEYSYNC, @@ -145,6 +147,10 @@ struct SVehicleComponentData bool m_bVisible; }; +static const std::array g_vehicleTypePrefixes = { + "Flying a UFO around", "Cruising around", "Riding the waves of", "Riding the train in", "Flying around", "Flying around", + "Riding around", "Monster truckin' around", "Quaddin' around", "Bunny hopping around", "Doing weird stuff in"}; + class CClientVehicle : public CClientStreamElement { DECLARE_CLASS(CClientVehicle, CClientStreamElement) diff --git a/Client/mods/deathmatch/logic/CPacketHandler.cpp b/Client/mods/deathmatch/logic/CPacketHandler.cpp index 79e5118cd7..51cad6aed4 100644 --- a/Client/mods/deathmatch/logic/CPacketHandler.cpp +++ b/Client/mods/deathmatch/logic/CPacketHandler.cpp @@ -324,6 +324,7 @@ void CPacketHandler::Packet_ServerJoined(NetBitStreamInterface& bitStream) // 2 - URL // unsigned short (2) - HTTP Download URL Size // unsigned char (X) - HTTP Download URL + // unsigned char (X) - Server name // Make sure any existing messageboxes are hided g_pCore->RemoveMessageBox(); @@ -464,6 +465,23 @@ void CPacketHandler::Packet_ServerJoined(NetBitStreamInterface& bitStream) g_pClientGame->m_pLocalPlayer->CallEvent("onClientPlayerJoin", Arguments, true); g_pCore->UpdateRecentlyPlayed(); + + if (g_pNet->CanServerBitStream((eBitStreamVersion::CPlayerJoinCompletePacket_ServerName))) + { + auto discord = g_pCore->GetDiscord(); + if (discord && discord->IsDiscordRPCEnabled()) + { + std::string serverName; + bitStream.ReadString(serverName); + + if (serverName.length() > 0) + { + g_pCore->SetLastConnectedServerName(serverName); + discord->SetPresenceDetails(serverName.c_str(), false); + } + } + } + } void CPacketHandler::Packet_ServerDisconnected(NetBitStreamInterface& bitStream) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp index 74a3cd38bf..ad88e97736 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.cpp @@ -20,6 +20,9 @@ void CLuaDiscordDefs::LoadFunctions() {"setDiscordRichPresenceAsset", ArgumentParser}, {"setDiscordRichPresenceSmallAsset", ArgumentParser}, {"setDiscordRichPresenceButton", ArgumentParser}, + {"setDiscordRichPresenceStartTime", ArgumentParser}, + {"setDiscordRichPresenceEndTime", ArgumentParser}, + {"setDiscordRichPresencePartySize", ArgumentParser}, {"resetDiscordRichPresenceData", ArgumentParser}, {"isDiscordRichPresenceConnected", ArgumentParser < IsDiscordRPCConnected>}, @@ -40,6 +43,9 @@ void CLuaDiscordDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "setAsset", "setDiscordRichPresenceAsset"); lua_classfunction(luaVM, "setSmallAsset", "setDiscordRichPresenceSmallAsset"); lua_classfunction(luaVM, "setButton", "setDiscordRichPresenceButton"); + lua_classfunction(luaVM, "setStartTime", "setDiscordRichPresenceStartTime"); + lua_classfunction(luaVM, "setEndTime", "setDiscordRichPresenceEndTime"); + lua_classfunction(luaVM, "setPartySize", "setDiscordRichPresencePartySize"); lua_classfunction(luaVM, "isConnected", "isDiscordRichPresenceConnected"); //lua_classfunction(luaVM, "setAppID", "setDiscordRichPresenceAppID"); @@ -62,8 +68,8 @@ 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"); + if (stateLength > 128) + throw std::invalid_argument("State must be less than/equal to 128"); auto discord = g_pCore->GetDiscord(); @@ -98,8 +104,8 @@ 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"); + if (detailsLength > 128) + throw std::invalid_argument("Details length must be less than/equal to 128"); auto discord = g_pCore->GetDiscord(); @@ -109,21 +115,76 @@ bool CLuaDiscordDefs::SetDetails(std::string strDetails) if (discord->IsDiscordCustomDetailsDisallowed()) return false; - if (!discord->SetPresenceDetails(strDetails.c_str())) + if (!discord->SetPresenceDetails(strDetails.c_str(), true)) return false; return true; } +bool CLuaDiscordDefs::SetStartTime(unsigned long ulTime) +{ + unsigned long ulSecondsSinceEpoch = time(nullptr) + ulTime; + + if (ulTime == 0) + ulSecondsSinceEpoch = 0; + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled()) + return false; + + if (discord->IsDiscordCustomDetailsDisallowed()) + return false; + + discord->SetPresenceStartTimestamp(ulSecondsSinceEpoch); + return true; +} + +bool CLuaDiscordDefs::SetEndTime(unsigned long ulTime) +{ + unsigned long ulSecondsSinceEpoch = time(nullptr) + ulTime; + + if (ulTime == 0) + ulSecondsSinceEpoch = 0; + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled()) + return false; + + if (discord->IsDiscordCustomDetailsDisallowed()) + return false; + + discord->SetPresenceEndTimestamp(ulSecondsSinceEpoch); + return true; +} + +bool CLuaDiscordDefs::SetPartySize(int iSize, int iMax) +{ + if (iSize > iMax) + throw std::invalid_argument("Party size must be less than or equal to max party size"); + + auto discord = g_pCore->GetDiscord(); + + if (!discord || !discord->IsDiscordRPCEnabled()) + return false; + + if (discord->IsDiscordCustomDetailsDisallowed()) + return false; + + discord->SetPresencePartySize(iSize, iMax); + 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"); + if (assetLength > 32) + throw std::invalid_argument("Asset name length must be less than/equal to 32"); + if (assetTextLength > 128) + throw std::invalid_argument("Asset text length must be less than/equal to 128"); auto discord = g_pCore->GetDiscord(); @@ -159,10 +220,10 @@ bool CLuaDiscordDefs::SetButtons(unsigned short int iIndex, std::string szName, 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 (nameLength > 32) + throw std::invalid_argument("Button name length must be less than/equal to 32"); + if (urlLength > 128) + throw std::invalid_argument("Button URL length must be 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"); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h index fb76298eec..2d5f0574a5 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaDiscordDefs.h @@ -25,6 +25,9 @@ class CLuaDiscordDefs : public CLuaDefs 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 SetStartTime(unsigned long ulTime); + static bool SetEndTime(unsigned long ulTime); + static bool SetPartySize(int iMin, int iMax); static bool IsDiscordRPCConnected(); }; diff --git a/Client/sdk/core/CCoreInterface.h b/Client/sdk/core/CCoreInterface.h index fe40988f94..25c578a25a 100644 --- a/Client/sdk/core/CCoreInterface.h +++ b/Client/sdk/core/CCoreInterface.h @@ -41,17 +41,21 @@ enum eCoreVersion }; #ifndef WITH_TIMING_CHECKPOINTS - #define WITH_TIMING_CHECKPOINTS 1 // Comment this line to remove timing checkpoint code + #define WITH_TIMING_CHECKPOINTS 1 // Comment this line to remove timing checkpoint code #endif #if WITH_TIMING_CHECKPOINTS - #define IS_TIMING_CHECKPOINTS() g_pCore->IsTimingCheckpoints () - #define TIMING_CHECKPOINT(x) g_pCore->OnTimingCheckpoint ( x ) - #define TIMING_DETAIL(x) g_pCore->OnTimingDetail ( x ) + #define IS_TIMING_CHECKPOINTS() g_pCore->IsTimingCheckpoints() + #define TIMING_CHECKPOINT(x) g_pCore->OnTimingCheckpoint(x) + #define TIMING_DETAIL(x) g_pCore->OnTimingDetail(x) #else - #define IS_TIMING_CHECKPOINTS() (false) - #define TIMING_CHECKPOINT(x) {} - #define TIMING_DETAIL(x) {} + #define IS_TIMING_CHECKPOINTS() (false) + #define TIMING_CHECKPOINT(x) \ + { \ + } + #define TIMING_DETAIL(x) \ + { \ + } #endif class CCoreInterface @@ -61,22 +65,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 std::shared_ptr GetDiscord() = 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; @@ -185,6 +189,9 @@ class CCoreInterface virtual void SetCustomStreamingMemory(size_t sizeBytes) = 0; virtual bool IsUsingCustomStreamingMemorySize() = 0; virtual size_t GetStreamingMemory() = 0; + + virtual const SString& GetLastConnectedServerName() const = 0; + virtual void SetLastConnectedServerName(const SString& strServerName) = 0; }; class CClientTime diff --git a/Client/sdk/core/CDiscordInterface.h b/Client/sdk/core/CDiscordInterface.h index 8b3394fe30..0214a298b9 100644 --- a/Client/sdk/core/CDiscordInterface.h +++ b/Client/sdk/core/CDiscordInterface.h @@ -22,7 +22,9 @@ class CDiscordInterface 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 void SetPresenceEndTimestamp(const unsigned long ulEnd) = 0; virtual bool SetPresenceButtons(unsigned short int iIndex, const char* szName, const char* szUrl) = 0; + virtual void SetPresencePartySize(int iSize, int iMax) = 0; //virtual void SetPresenceEndTimestamp(const unsigned long ulEnd) = 0; virtual bool SetDiscordRPCEnabled(bool bEnabled = false) = 0; diff --git a/Client/sdk/game/CTaskManager.h b/Client/sdk/game/CTaskManager.h index 7db4d08ef2..3467f34928 100644 --- a/Client/sdk/game/CTaskManager.h +++ b/Client/sdk/game/CTaskManager.h @@ -23,7 +23,7 @@ enum TASK_PRIORITY_MAX }; -enum +enum eSecondaryTaskType { TASK_SECONDARY_ATTACK = 0, // want duck to be after attack TASK_SECONDARY_DUCK, // because attack controls ducking movement diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index f6af260975..a08af9c801 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -1308,7 +1308,7 @@ void CGame::JoinPlayer(CPlayer& Player) Player.Send(CPlayerJoinCompletePacket( Player.GetID(), m_pMapManager->GetRootElement()->GetID(), m_pMainConfig->GetHTTPDownloadType(), m_pMainConfig->GetHTTPPort(), m_pMainConfig->GetHTTPDownloadURL().c_str(), m_pMainConfig->GetHTTPMaxConnectionsPerClient(), m_pMainConfig->GetEnableClientChecks(), - m_pMainConfig->IsVoiceEnabled(), m_pMainConfig->GetVoiceSampleRate(), m_pMainConfig->GetVoiceQuality(), m_pMainConfig->GetVoiceBitrate())); + m_pMainConfig->IsVoiceEnabled(), m_pMainConfig->GetVoiceSampleRate(), m_pMainConfig->GetVoiceQuality(), m_pMainConfig->GetVoiceBitrate(), m_pMainConfig->GetServerName().c_str())); marker.Set("CPlayerJoinCompletePacket"); diff --git a/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.cpp b/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.cpp index a61b9c2f81..922b3494f0 100644 --- a/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.cpp @@ -27,12 +27,13 @@ CPlayerJoinCompletePacket::CPlayerJoinCompletePacket() m_ucSampleRate = 1; m_ucQuality = 4; m_uiBitrate = 0; + m_szServerName = ""; } CPlayerJoinCompletePacket::CPlayerJoinCompletePacket(ElementID PlayerID, ElementID RootElementID, eHTTPDownloadType ucHTTPDownloadType, unsigned short usHTTPDownloadPort, const char* szHTTPDownloadURL, int iHTTPMaxConnectionsPerClient, int iEnableClientChecks, bool bVoiceEnabled, unsigned char ucSampleRate, unsigned char ucVoiceQuality, - unsigned int uiBitrate) + unsigned int uiBitrate, const char* szServerName) { m_PlayerID = PlayerID; m_RootElementID = RootElementID; @@ -43,6 +44,7 @@ CPlayerJoinCompletePacket::CPlayerJoinCompletePacket(ElementID PlayerID, Element m_ucSampleRate = ucSampleRate; m_ucQuality = ucVoiceQuality; m_uiBitrate = uiBitrate; + m_szServerName = szServerName; switch (m_ucHTTPDownloadType) { @@ -120,5 +122,8 @@ bool CPlayerJoinCompletePacket::Write(NetBitStreamInterface& BitStream) const break; } + if (BitStream.Can(eBitStreamVersion::CPlayerJoinCompletePacket_ServerName)) + BitStream.WriteString(m_szServerName); + return true; } diff --git a/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.h b/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.h index 74d27b6ae9..e50f89f9e4 100644 --- a/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.h +++ b/Server/mods/deathmatch/logic/packets/CPlayerJoinCompletePacket.h @@ -21,7 +21,7 @@ class CPlayerJoinCompletePacket final : public CPacket CPlayerJoinCompletePacket(); CPlayerJoinCompletePacket(ElementID PlayerID, ElementID RootElementID, eHTTPDownloadType ucHTTPDownloadType, unsigned short usHTTPDownloadPort, const char* szHTTPDownloadURL, int iHTTPMaxConnectionsPerClient, int iEnableClientChecks, bool bVoiceEnabled, - unsigned char ucSampleRate, unsigned char ucVoiceQuality, unsigned int uiBitrate); + unsigned char ucSampleRate, unsigned char ucVoiceQuality, unsigned int uiBitrate, const char* szServerName); ePacketID GetPacketID() const { return PACKET_ID_SERVER_JOINEDGAME; }; unsigned long GetFlags() const { return PACKET_HIGH_PRIORITY | PACKET_RELIABLE | PACKET_SEQUENCED; }; @@ -40,4 +40,5 @@ class CPlayerJoinCompletePacket final : public CPacket unsigned char m_ucSampleRate; unsigned char m_ucQuality; unsigned int m_uiBitrate; + const char* m_szServerName; }; diff --git a/Shared/sdk/net/bitstream.h b/Shared/sdk/net/bitstream.h index 24bd330e81..3924ebbf16 100644 --- a/Shared/sdk/net/bitstream.h +++ b/Shared/sdk/net/bitstream.h @@ -536,6 +536,10 @@ enum class eBitStreamVersion : unsigned short // 2023-09-09 WorldSpecialProperty_FireballDestruct, + // Send server name to player in CPlayerJoinCompletePacket + // 2023-10-12 + CPlayerJoinCompletePacket_ServerName, + // This allows us to automatically increment the BitStreamVersion when things are added to this enum. // Make sure you only add things above this comment. Next,