Skip to content
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

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
eb21260
Add Discord Network support.
Buggem Dec 10, 2024
0a1244b
Dynamically load Discord Game SDK with `SDL_LoadObject()` to avoid CM…
Buggem Dec 12, 2024
7975b27
Patch potential crash scenarios and remove warnings.
Buggem Dec 12, 2024
76d5738
Replace `strncpy` with `SDL_strncpy`
Buggem Dec 12, 2024
76d4368
Remove unneeded headerfiles, .vscode folder, and move comment
Buggem Dec 12, 2024
ce111cf
Make Discord not required to exist - thanks @Daaaav
Buggem Dec 12, 2024
7d3e7db
Avoid segfault if Discord is not detected, remove useless headerfiles
Buggem Dec 13, 2024
17fea40
Remove localisation features from the RPC, as people on English Disco…
Buggem Dec 14, 2024
459437c
Remove placeholder
Buggem Dec 15, 2024
7fcba85
Add @Daaaav's changes (full changelog below)
Buggem Dec 15, 2024
5cbf03d
Fix bad implementation of inversion of discordDetected
Buggem Dec 15, 2024
132c504
finish off naming convention = type*
Buggem Dec 17, 2024
29708db
Finish @Daaaav's requests :D
Buggem Dec 19, 2024
3b5108e
More requests (see full changelog below)
Buggem Dec 21, 2024
bef03b6
consistancy
Buggem Dec 21, 2024
d35fb29
maybe remove console spam (seperate commit to consider reverts)
Buggem Dec 21, 2024
9c05ffe
Finish formatting
Buggem Dec 22, 2024
731f2ce
Merge branch 'TerryCavanagh:master' into discordrpc
Buggem Dec 31, 2024
6e5fed0
Add more names for different areas of the game IG
Buggem Jan 1, 2025
bf9dd78
Make the RPC less invasive, only change when we NEED to.
Buggem Jan 25, 2025
fba8f7d
Strip unused stdio.h headerfile [redo]
Buggem Jan 25, 2025
28a38bd
Commit flibit's changes **needs polish**
Buggem Jan 26, 2025
ec132ac
Polish Flibit's changes to work. (PLS FIX POINTERS D:)
Buggem Jan 26, 2025
62ecc83
Patch out pointer control (new method sucks but ok I guess)
Buggem Jan 26, 2025
4a12fae
small commit: `type* varname` convention
Buggem Jan 26, 2025
6bc5649
Merge branch 'TerryCavanagh:master' into discordrpc
Buggem Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions desktop_version/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ option(BUNDLE_DEPENDENCIES "Use bundled TinyXML-2, PhysicsFS, and FAudio (if dis

option(STEAM "Use the Steam API" OFF)
option(GOG "Use the GOG API" OFF)
option(DISCORD "Use the Discord API" OFF)

option(OFFICIAL_BUILD "Compile an official build of the game" OFF)

Expand All @@ -19,6 +20,7 @@ option(MAKEANDPLAY "Compile a version of the game without the main campaign (pro
if(OFFICIAL_BUILD AND NOT MAKEANDPLAY)
set(STEAM ON)
set(GOG ON)
set(DISCORD ON)
endif()

option(REMOVE_ABSOLUTE_PATHS "If supported by the compiler, replace all absolute paths to source directories compiled into the binary (if any) with relative paths" ON)
Expand Down Expand Up @@ -133,6 +135,9 @@ endif()
if(GOG)
list(APPEND VVV_C_SRC src/GOGNetwork.c)
endif()
if(DISCORD)
list(APPEND VVV_C_SRC src/DiscordNetwork.c)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
list(APPEND VVV_C_SRC src/SDL_uikit_main.c)
endif()
Expand Down Expand Up @@ -210,6 +215,9 @@ endif()
if(GOG)
target_compile_definitions(VVVVVV PRIVATE -DGOG_NETWORK)
endif()
if(DISCORD)
target_compile_definitions(VVVVVV PRIVATE -DDISCORD_NETWORK)
endif()

set(XML2_SRC
../third_party/tinyxml2/tinyxml2.cpp
Expand Down
188 changes: 188 additions & 0 deletions desktop_version/src/DiscordNetwork.c
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!
Comment on lines +25 to +27
Copy link
Collaborator

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...?

Copy link
Contributor

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:

If you're integrating our SDK into an already-released game, there's a good chance that we may already have an application in our database for your game! Reach out to our Dev Support to learn more

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?)

Copy link
Author

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)

Copy link
Contributor

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.

Copy link
Collaborator

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

Copy link
Contributor

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.



struct DISCORD_application {
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__)
Copy link
Contributor

Choose a reason for hiding this comment

The 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, &params, &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);
}

Copy link
Contributor

Choose a reason for hiding this comment

The 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
10 changes: 8 additions & 2 deletions desktop_version/src/GOGNetwork.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ void GOG_shutdown(void)
{
}

void GOG_update(void)
int32_t GOG_update(void)
{
return 0;
}

void GOG_unlockAchievement(const char *name)
void GOG_unlockAchievement(const char* name)
{
}

void GOG_setRPC(const char* area, const char* roomname)
{
}


#endif /* MAKEANDPLAY */
53 changes: 45 additions & 8 deletions desktop_version/src/Network.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#ifdef GOG_NETWORK
#undef GOG_NETWORK
#endif
#ifdef DISCORD_NETWORK
#undef DISCORD_NETWORK
#endif
#endif

#ifdef STEAM_NETWORK
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -89,19 +107,25 @@ 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() )
Copy link
Collaborator

Choose a reason for hiding this comment

The 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)
void NETWORK_unlockAchievement(const char* name)
{
#if NUM_BACKENDS > 0
int32_t i;
Expand All @@ -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
}

4 changes: 3 additions & 1 deletion desktop_version/src/Network.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ int32_t NETWORK_init(void);

void NETWORK_shutdown(void);

void NETWORK_update(void);
int32_t NETWORK_update(void);

void NETWORK_unlockAchievement(const char *name);

int32_t NETWORK_getAchievementProgress(const char *name);

void NETWORK_setAchievementProgress(const char *name, int32_t stat);

void NETWORK_setRPC(const char *area, const char *roomname);

#ifdef __cplusplus
}
#endif
Expand Down
Loading