1#include <base/system.h>
2#include <engine/discord.h>
3
4// Hack for universal binary builds on macOS: Ignore arm64 until Discord
5// releases a native arm64 SDK for macOS.
6
7#if defined(CONF_DISCORD) && !(defined(CONF_ARCH_ARM64) && defined(CONF_PLATFORM_MACOS))
8#include <discord_game_sdk.h>
9
10typedef enum EDiscordResult DISCORD_API (*FDiscordCreate)(DiscordVersion, struct DiscordCreateParams *, struct IDiscordCore **);
11
12#if defined(CONF_DISCORD_DYNAMIC)
13#include <dlfcn.h>
14FDiscordCreate GetDiscordCreate()
15{
16 void *pSdk = dlopen("discord_game_sdk.so", RTLD_NOW);
17 if(!pSdk)
18 {
19 return nullptr;
20 }
21 return (FDiscordCreate)dlsym(pSdk, "DiscordCreate");
22}
23#else
24FDiscordCreate GetDiscordCreate()
25{
26 return DiscordCreate;
27}
28#endif
29
30class CDiscord : public IDiscord
31{
32 IDiscordCore *m_pCore;
33 IDiscordActivityEvents m_ActivityEvents;
34 IDiscordActivityManager *m_pActivityManager;
35
36public:
37 bool Init(FDiscordCreate pfnDiscordCreate)
38 {
39 m_pCore = 0;
40 mem_zero(&m_ActivityEvents, sizeof(m_ActivityEvents));
41 m_pActivityManager = 0;
42
43 DiscordCreateParams Params;
44 DiscordCreateParamsSetDefault(&Params);
45
46 Params.client_id = 752165779117441075; // DDNet
47 Params.flags = EDiscordCreateFlags::DiscordCreateFlags_NoRequireDiscord;
48 Params.event_data = this;
49 Params.activity_events = &m_ActivityEvents;
50 int Error = pfnDiscordCreate(DISCORD_VERSION, &Params, &m_pCore);
51 if(Error != DiscordResult_Ok)
52 {
53 dbg_msg("discord", "error initializing discord instance, error=%d", Error);
54 return true;
55 }
56
57 m_pActivityManager = m_pCore->get_activity_manager(m_pCore);
58 ClearGameInfo();
59 return false;
60 }
61 void Update() override
62 {
63 m_pCore->run_callbacks(m_pCore);
64 }
65 void ClearGameInfo() override
66 {
67 DiscordActivity Activity;
68 mem_zero(&Activity, sizeof(DiscordActivity));
69 str_copy(Activity.assets.large_image, "ddnet_logo", sizeof(Activity.assets.large_image));
70 str_copy(Activity.assets.large_text, "DDNet logo", sizeof(Activity.assets.large_text));
71 Activity.timestamps.start = time_timestamp();
72 str_copy(Activity.details, "Offline", sizeof(Activity.details));
73 m_pActivityManager->update_activity(m_pActivityManager, &Activity, 0, 0);
74 }
75 void SetGameInfo(const NETADDR &ServerAddr, const char *pMapName, bool AnnounceAddr) override
76 {
77 DiscordActivity Activity;
78 mem_zero(&Activity, sizeof(DiscordActivity));
79 str_copy(Activity.assets.large_image, "ddnet_logo", sizeof(Activity.assets.large_image));
80 str_copy(Activity.assets.large_text, "DDNet logo", sizeof(Activity.assets.large_text));
81 Activity.timestamps.start = time_timestamp();
82 str_copy(Activity.details, "Online", sizeof(Activity.details));
83 str_copy(Activity.state, pMapName, sizeof(Activity.state));
84 m_pActivityManager->update_activity(m_pActivityManager, &Activity, 0, 0);
85 }
86};
87
88IDiscord *CreateDiscordImpl()
89{
90 FDiscordCreate pfnDiscordCreate = GetDiscordCreate();
91 if(!pfnDiscordCreate)
92 {
93 return 0;
94 }
95 CDiscord *pDiscord = new CDiscord();
96 if(pDiscord->Init(pfnDiscordCreate))
97 {
98 delete pDiscord;
99 return 0;
100 }
101 return pDiscord;
102}
103#else
104IDiscord *CreateDiscordImpl()
105{
106 return 0;
107}
108#endif
109
110class CDiscordStub : public IDiscord
111{
112 void Update() override {}
113 void ClearGameInfo() override {}
114 void SetGameInfo(const NETADDR &ServerAddr, const char *pMapName, bool AnnounceAddr) override {}
115};
116
117IDiscord *CreateDiscord()
118{
119 IDiscord *pDiscord = CreateDiscordImpl();
120 if(pDiscord)
121 {
122 return pDiscord;
123 }
124 return new CDiscordStub();
125}
126