-
Notifications
You must be signed in to change notification settings - Fork 559
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Discord Game SDK support #1217
base: master
Are you sure you want to change the base?
Changes from 21 commits
eb21260
0a1244b
7975b27
76d5738
76d4368
ce111cf
7d3e7db
17fea40
459437c
7fcba85
5cbf03d
132c504
29708db
3b5108e
bef03b6
d35fb29
9c05ffe
731f2ce
6e5fed0
bf9dd78
fba8f7d
28a38bd
ec132ac
62ecc83
4a12fae
6bc5649
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
#include "MakeAndPlay.h" | ||
|
||
#ifndef MAKEANDPLAY | ||
|
||
#include <stdint.h> | ||
#include <stdbool.h> | ||
#include <SDL.h> | ||
|
||
#include "Vlogging.h" | ||
|
||
// Library includes | ||
#include "discord_game_sdk.h" | ||
|
||
#if defined(_WIN32) | ||
#define DISCORD_LIBRARY "discord_game_sdk.dll" | ||
#elif defined(__APPLE__) | ||
#define DISCORD_LIBRARY "libdiscord_game_sdk.dylib" | ||
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__DragonFly__) | ||
#define DISCORD_LIBRARY "libdiscord_game_sdk.so" | ||
#else | ||
#error DISCORD_LIBRARY: Unrecognized platform! | ||
#endif | ||
|
||
|
||
#define DISCORD_CLIENT_ID 1315544357532729447 | ||
|
||
// TO TERRY/FLIBIT: You can create your own Discord instance at the Discord Developer Portal. This ID belongs to me, so just be aware that if my account was to get hacked, VVVVVV RPC would too. Use your own! | ||
|
||
|
||
struct DISCORD_application { | ||
Buggem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
struct IDiscordCore* core; | ||
struct IDiscordActivityManager* activityMan; | ||
} app; | ||
|
||
static struct DiscordActivity activity; | ||
|
||
static bool discordDetected = false; | ||
static bool discordPostInit = false; | ||
|
||
|
||
static void* libHandle = NULL; | ||
|
||
|
||
|
||
#define FUNC_LIST \ | ||
FOREACH_FUNC(enum EDiscordResult, DiscordCreate, (DiscordVersion, struct DiscordCreateParams*, struct IDiscordCore**)) | ||
|
||
|
||
|
||
#define FOREACH_FUNC(rettype, name, params) static rettype (*name) params = NULL; | ||
FUNC_LIST | ||
#undef FOREACH_FUNC | ||
|
||
bool DISCORD_REQUIRE(int x) | ||
{ | ||
if (!discordDetected && discordPostInit) | ||
{ | ||
return false; | ||
} | ||
if (x != DiscordResult_Ok) | ||
{ | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
|
||
|
||
static void ClearPointers(void) | ||
{ | ||
SDL_UnloadObject(libHandle); | ||
libHandle = NULL; | ||
#define FOREACH_FUNC(rettype, name, params) name = NULL; | ||
FUNC_LIST | ||
#undef FOREACH_FUNC | ||
} | ||
|
||
void DISCORD_shutdown(void) | ||
{ | ||
if (libHandle != NULL) | ||
{ | ||
app.core->destroy(app.core); | ||
ClearPointers(); | ||
} | ||
} | ||
|
||
int32_t DISCORD_init(void) | ||
{ | ||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) || defined(__DragonFly__) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be rewritten to check for OSes that Discord does support, rather than ones it doesn't. |
||
return 0; | ||
#endif | ||
libHandle = SDL_LoadObject(DISCORD_LIBRARY); | ||
if (libHandle == NULL) | ||
{ | ||
vlog_info(DISCORD_LIBRARY " not found!"); | ||
vlog_debug("Can't load object %s : %s\n", DISCORD_LIBRARY, SDL_GetError()); | ||
return 0; | ||
} | ||
|
||
#define FOREACH_FUNC(rettype, name, params) \ | ||
name = (rettype (*) params) (intptr_t) SDL_LoadFunction(libHandle, #name); \ | ||
if (!name) \ | ||
{ \ | ||
vlog_error(DISCORD_LIBRARY " symbol " #name " not found!"); \ | ||
ClearPointers(); \ | ||
return 0; \ | ||
} | ||
FUNC_LIST | ||
#undef FOREACH_FUNC | ||
SDL_memset(&app, 0, sizeof(app)); | ||
|
||
struct DiscordCreateParams params; | ||
params.client_id = DISCORD_CLIENT_ID; | ||
params.flags = DiscordCreateFlags_NoRequireDiscord; | ||
|
||
if (!DISCORD_REQUIRE(DiscordCreate(DISCORD_VERSION, ¶ms, &app.core))) | ||
{ // Discord's API couldn't initialise, so just ignore it | ||
discordPostInit = true; // even if it fails, set post init to true. | ||
vlog_debug("Discord API failed to initialise!"); | ||
discordDetected = false; | ||
return 0; | ||
} | ||
|
||
if (app.core == NULL) | ||
{ | ||
discordPostInit = true; | ||
vlog_debug("app.core == null. DiscordCreate failed with a positive result?\nCheck DISCORD_REQUIRE for bugs."); | ||
discordDetected = false; | ||
return 0; | ||
} | ||
discordPostInit = true; | ||
|
||
// Placeholder, is this really nesaccary | ||
SDL_strlcpy(activity.assets.large_image, "vvvvvv", sizeof(activity.assets.large_image)); | ||
SDL_strlcpy(activity.assets.large_text, "Outside Dimension VVVVVV", sizeof(activity.assets.large_text)); | ||
|
||
app.activityMan = app.core->get_activity_manager(app.core); | ||
app.activityMan->update_activity(app.activityMan, &activity, NULL, NULL); | ||
|
||
|
||
discordDetected = true; | ||
return 1; | ||
} | ||
|
||
int32_t DISCORD_update(void) | ||
{ | ||
if (libHandle == NULL) | ||
{ | ||
// no Discord or just shutdown | ||
return 0; | ||
} | ||
if (app.core == NULL || !discordDetected) | ||
{ | ||
// No Discord | ||
return 0; | ||
} | ||
|
||
if (!DISCORD_REQUIRE(app.core->run_callbacks(app.core))) | ||
{ | ||
// Something or other is wrong, but do we care? | ||
return 0; | ||
} | ||
return 1; | ||
} | ||
void DISCORD_unlockAchievement(const char *name) | ||
{ | ||
// No "achievements" in Discord | ||
} | ||
|
||
void DISCORD_setRPC(const char *area, const char *roomname) | ||
{ | ||
if (app.activityMan == NULL) | ||
{ | ||
vlog_debug("No activityMan! - it\'s fine, we can recreate this" ); | ||
app.activityMan = app.core->get_activity_manager(app.core); | ||
} | ||
if (SDL_strcmp(activity.state, roomname) != 0 || SDL_strcmp(activity.assets.large_text, area) != 0) | ||
{ | ||
SDL_strlcpy(activity.state, roomname, sizeof(activity.state)); | ||
SDL_strlcpy(activity.assets.large_image, "vvvvvv", sizeof(activity.assets.large_image)); | ||
SDL_strlcpy(activity.assets.large_text, area, sizeof(activity.assets.large_text)); | ||
|
||
app.activityMan->update_activity(app.activityMan, &activity, NULL, NULL); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Newlines in this file seem generally inconsistent. This should probably be cleaned up. |
||
} | ||
|
||
#endif // MakeAndPlay |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,9 @@ | |
#ifdef GOG_NETWORK | ||
#undef GOG_NETWORK | ||
#endif | ||
#ifdef DISCORD_NETWORK | ||
#undef DISCORD_NETWORK | ||
#endif | ||
#endif | ||
|
||
#ifdef STEAM_NETWORK | ||
|
@@ -22,28 +25,38 @@ | |
#else | ||
#define GOG_NUM 0 | ||
#endif | ||
#ifdef DISCORD_NETWORK | ||
#define DISCORD_NUM 1 | ||
#else | ||
#define DISCORD_NUM 0 | ||
#endif | ||
|
||
#define NUM_BACKENDS (STEAM_NUM+GOG_NUM) | ||
#define NUM_BACKENDS (STEAM_NUM+GOG_NUM+DISCORD_NUM) | ||
#define DECLARE_BACKEND(name) \ | ||
int32_t name##_init(void); \ | ||
void name##_shutdown(void); \ | ||
void name##_update(void); \ | ||
void name##_unlockAchievement(const char *name); | ||
int32_t name##_update(void); \ | ||
void name##_unlockAchievement(const char *name); \ | ||
void name##_setRPC(const char *area, const char *roomname); | ||
#ifdef STEAM_NETWORK | ||
DECLARE_BACKEND(STEAM) | ||
#endif | ||
#ifdef GOG_NETWORK | ||
DECLARE_BACKEND(GOG) | ||
#endif | ||
#ifdef DISCORD_NETWORK | ||
DECLARE_BACKEND(DISCORD) | ||
#endif | ||
#undef DECLARE_BACKEND | ||
|
||
typedef struct NetworkBackend | ||
{ | ||
int32_t IsInit; | ||
int32_t (*Init)(void); | ||
void (*Shutdown)(void); | ||
void (*Update)(void); | ||
int32_t (*Update)(void); | ||
void (*UnlockAchievement)(const char*); | ||
void (*SetRPC)(const char*, const char*); | ||
} NetworkBackend; | ||
|
||
#if NUM_BACKENDS > 0 | ||
|
@@ -57,13 +70,18 @@ int NETWORK_init(void) | |
backends[index].Init = name##_init; \ | ||
backends[index].Shutdown = name##_shutdown; \ | ||
backends[index].Update = name##_update; \ | ||
backends[index].UnlockAchievement = name##_unlockAchievement; | ||
backends[index].UnlockAchievement = name##_unlockAchievement; \ | ||
backends[index].SetRPC = name##_setRPC; | ||
|
||
#ifdef STEAM_NETWORK | ||
ASSIGN_BACKEND(STEAM, 0) | ||
#endif | ||
#ifdef GOG_NETWORK | ||
ASSIGN_BACKEND(GOG, STEAM_NUM) | ||
#endif | ||
#ifdef DISCORD_NETWORK | ||
ASSIGN_BACKEND(DISCORD, STEAM_NUM+GOG_NUM) | ||
#endif | ||
#undef ASSIGN_BACKEND | ||
#if NUM_BACKENDS > 0 | ||
for (i = 0; i < NUM_BACKENDS; i += 1) | ||
|
@@ -89,16 +107,22 @@ void NETWORK_shutdown(void) | |
#endif | ||
} | ||
|
||
void NETWORK_update(void) | ||
|
||
int32_t NETWORK_update(void) | ||
{ | ||
int32_t result = 0; | ||
#if NUM_BACKENDS > 0 | ||
int32_t i; | ||
for (i = 0; i < NUM_BACKENDS; i += 1) | ||
if (backends[i].IsInit) | ||
{ | ||
backends[i].Update(); | ||
if ( backends[i].Update() ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to worry about Update status too much - as long as we do a good job with SetRPC this can stay as-is. |
||
{ | ||
result |= (1 << i); | ||
} | ||
} | ||
#endif | ||
return result; | ||
} | ||
|
||
void NETWORK_unlockAchievement(const char *name) | ||
|
@@ -114,3 +138,16 @@ void NETWORK_unlockAchievement(const char *name) | |
UNUSED(name); | ||
#endif | ||
} | ||
|
||
void NETWORK_setRPC(const char *area, const char *roomname) | ||
{ | ||
#if NUM_BACKENDS > 0 | ||
int32_t i; | ||
for (i = 0; i < NUM_BACKENDS; i += 1) | ||
if (backends[i].IsInit) | ||
{ | ||
backends[i].SetRPC(area, roomname); | ||
} | ||
#endif | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TerryCavanagh, does this already exist...?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, the Discord docs do say this:
I wonder whether the reasons to use the ID they already have are good enough reasons to bother lol (maybe so it smoothly takes over the "Playing VVVVVV", "last played 2 days ago", etc that was already showing without rich presence?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Daaaav @flibitijibibo I have sent a message to Discord Support to see if this is true for VVVVVV. Hopefully I get a response before the 15th anniversary of VVVVVV. (I'm hoping this will be released then)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure an application already exists for VVVVVV, as it existed on the Discord Game Store back when that existed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to lock this in before we can merge, so whoever can find this number should patch this line ASAP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The automatically assigned application ID is
491427544134975498
, but this doesn't currently work for rich presence. I'm not sure if the Discord Store release had a separate application ID.