Skip to content

Commit f0382af

Browse files
committed
feat(online): Add Discord Rich Presence
1 parent b7cfeaf commit f0382af

11 files changed

Lines changed: 459 additions & 3 deletions

File tree

GeneralsMD/Code/GameEngine/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,8 @@ set(GAMEENGINE_SRC
11371137
Source/Precompiled/PreRTS.cpp
11381138

11391139
Include/GameNetwork/GeneralsOnline/json.hpp
1140+
Include/GameNetwork/GeneralsOnline/DiscordRichPresence.h
1141+
Include/GameNetwork/GeneralsOnline/Vendor/DiscordRPC/discord_rpc.h
11401142
Include/GameNetwork/GeneralsOnline/NetworkBitstream.h
11411143
Include/GameNetwork/GeneralsOnline/NetworkMesh.h
11421144
Include/GameNetwork/GeneralsOnline/NetworkPacket.h
@@ -1181,6 +1183,7 @@ set(GAMEENGINE_SRC
11811183
Include/GameNetwork/GeneralsOnline/Vendor/libcurl/typecheck-gcc.h
11821184
Include/GameNetwork/GeneralsOnline/Vendor/libcurl/urlapi.h
11831185
Include/GameNetwork/GeneralsOnline/Vendor/libcurl/websockets.h
1186+
Source/GameNetwork/GeneralsOnline/DiscordRichPresence.cpp
11841187
Source/GameNetwork/GeneralsOnline/NGMP_Helpers.cpp
11851188
Source/GameNetwork/GeneralsOnline/NGMPGame.cpp
11861189
Source/GameNetwork/GeneralsOnline/GeneralsOnline_Settings.cpp
@@ -1249,4 +1252,4 @@ target_precompile_headers(z_gameengine PRIVATE
12491252

12501253

12511254
add_compile_definitions(_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR)
1252-
add_compile_definitions(GENERALS_ONLINE)
1255+
add_compile_definitions(GENERALS_ONLINE)

GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class FunctionLexicon;
4949
class Radar;
5050
class WebBrowser;
5151
class ParticleSystemManager;
52+
class GeneralsOnlineDiscordRPC;
5253

5354
void TearDownGeneralsOnline();
5455

@@ -106,6 +107,7 @@ class GameEngine : public SubsystemInterface
106107

107108
Bool m_quitting; ///< true when we need to quit the game
108109
Bool m_isActive; ///< app has OS focus.
110+
GeneralsOnlineDiscordRPC* m_discordRichPresence;
109111
};
110112

111113
inline void GameEngine::setQuitting( Bool quitting ) { m_quitting = quitting; }
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#pragma once
2+
3+
#include "GameNetwork/GeneralsOnline/Vendor/DiscordRPC/discord_rpc.h"
4+
5+
#include <chrono>
6+
#include <string>
7+
8+
class NGMP_OnlineServices_LobbyInterface;
9+
10+
// TheSuperHackers @feature ultimate2000 19/07/2026 Adds Discord Rich Presence
11+
// for Generals Online lobbies and matches.
12+
class GeneralsOnlineDiscordRPC {
13+
public:
14+
GeneralsOnlineDiscordRPC() = default;
15+
~GeneralsOnlineDiscordRPC();
16+
17+
bool Initialize();
18+
void Shutdown();
19+
void Tick(const NGMP_OnlineServices_LobbyInterface *lobbyInterface);
20+
21+
private:
22+
using InitializeFunction = void (*)(const char *, DiscordEventHandlers *, int,
23+
const char *);
24+
using ShutdownFunction = void (*)();
25+
using RunCallbacksFunction = void (*)();
26+
using UpdatePresenceFunction = void (*)(const DiscordRichPresence *);
27+
using ClearPresenceFunction = void (*)();
28+
29+
struct PresenceData {
30+
std::string state;
31+
std::string details;
32+
std::string largeImageKey;
33+
std::string largeImageText;
34+
std::string smallImageKey;
35+
std::string smallImageText;
36+
std::string partyId;
37+
int partySize = 0;
38+
int partyMax = 0;
39+
40+
std::string Fingerprint() const;
41+
};
42+
43+
PresenceData
44+
BuildPresence(const NGMP_OnlineServices_LobbyInterface *lobbyInterface) const;
45+
void UpdatePresence(const PresenceData &data);
46+
void ResetFunctions();
47+
48+
void *m_module = nullptr;
49+
InitializeFunction m_initialize = nullptr;
50+
ShutdownFunction m_shutdown = nullptr;
51+
RunCallbacksFunction m_runCallbacks = nullptr;
52+
UpdatePresenceFunction m_updatePresence = nullptr;
53+
ClearPresenceFunction m_clearPresence = nullptr;
54+
bool m_initialized = false;
55+
std::string m_lastPresence;
56+
std::chrono::steady_clock::time_point m_nextPresenceUpdate;
57+
};

GeneralsMD/Code/GameEngine/Include/GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ class NGMP_OnlineServices_LobbyInterface
237237
return m_CurrentLobby;
238238
}
239239

240+
const LobbyEntry& GetCurrentLobby() const
241+
{
242+
return m_CurrentLobby;
243+
}
244+
240245
NGMPGame* GetCurrentGame()
241246
{
242247
return TheNGMPGame;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#pragma once
2+
3+
#include <stdint.h>
4+
5+
#ifdef __cplusplus
6+
extern "C" {
7+
#endif
8+
9+
typedef struct DiscordRichPresence {
10+
const char *state;
11+
const char *details;
12+
int64_t startTimestamp;
13+
int64_t endTimestamp;
14+
const char *largeImageKey;
15+
const char *largeImageText;
16+
const char *smallImageKey;
17+
const char *smallImageText;
18+
const char *partyId;
19+
int partySize;
20+
int partyMax;
21+
const char *matchSecret;
22+
const char *joinSecret;
23+
const char *spectateSecret;
24+
int8_t instance;
25+
} DiscordRichPresence;
26+
27+
typedef struct DiscordUser {
28+
const char *userId;
29+
const char *username;
30+
const char *discriminator;
31+
const char *avatar;
32+
} DiscordUser;
33+
34+
typedef struct DiscordEventHandlers {
35+
void (*ready)(const DiscordUser *request);
36+
void (*disconnected)(int errorCode, const char *message);
37+
void (*errored)(int errorCode, const char *message);
38+
void (*joinGame)(const char *joinSecret);
39+
void (*spectateGame)(const char *spectateSecret);
40+
void (*joinRequest)(const DiscordUser *request);
41+
} DiscordEventHandlers;
42+
43+
#ifdef __cplusplus
44+
}
45+
#endif

GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@
112112

113113
// GENERALS ONLINE
114114
#include "../OnlineServices_Init.h"
115+
#include "GameNetwork/GeneralsOnline/DiscordRichPresence.h"
116+
#include "GameNetwork/GeneralsOnline/OnlineServices_LobbyInterface.h"
115117
#include "GameNetwork/GameSpyOverlay.h"
116118
#include <chrono>
117119
#include "ww3d.h"
@@ -295,13 +297,17 @@ GameEngine::GameEngine()
295297
m_logicTimeAccumulator = 0.0f;
296298
m_quitting = FALSE;
297299
m_isActive = FALSE;
300+
m_discordRichPresence = nullptr;
298301

299302
_Module.Init(nullptr, ApplicationHInstance, nullptr);
300303
}
301304

302305
//-------------------------------------------------------------------------------------------------
303306
GameEngine::~GameEngine()
304307
{
308+
delete m_discordRichPresence;
309+
m_discordRichPresence = nullptr;
310+
305311
//extern std::vector<std::string> preloadTextureNamesGlobalHack;
306312
//preloadTextureNamesGlobalHack.clear();
307313

@@ -822,6 +828,9 @@ void GameEngine::init()
822828

823829
// NGMP_CHANGE: Init our settings
824830
NGMP_OnlineServicesManager::Settings.Initialize();
831+
832+
m_discordRichPresence = new GeneralsOnlineDiscordRPC();
833+
m_discordRichPresence->Initialize();
825834
}
826835

827836
/** -----------------------------------------------------------------------------------------------
@@ -990,6 +999,12 @@ void GameEngine::update()
990999
{
9911000
NGMP_OnlineServicesManager::GetInstance()->Tick();
9921001
}
1002+
1003+
if (m_discordRichPresence != nullptr)
1004+
{
1005+
m_discordRichPresence->Tick(
1006+
NGMP_OnlineServicesManager::GetInterface<NGMP_OnlineServices_LobbyInterface>());
1007+
}
9931008
}
9941009

9951010
const Bool canUpdate = canUpdateGameLogic(FramePacer::IgnoreFrozenTime | FramePacer::IgnoreHaltedGame);

0 commit comments

Comments
 (0)