Implementing Discord RPC

From Valve Developer Community
Jump to: navigation, search

To do: This implementation is fairly basic, so any edits or additions are greatly appreciated!


Implementing Discord RPC into the Source engine is really easy, and it makes your mod stand out more, as your in-game "presence" can be seen through Discord.

What is Discord RPC?

Discord RPC is a library for interfacing your game with a locally running Discord desktop client. It's known to work on Windows, macOS, and Linux. You can use the lib directly if you like, or use it as a guide to writing your own if it doesn't suit your game as is.


Below is an example using this article as a base, with obvious modifications:

DiscordRPCExample.png

The Installation

First off you'll need to download the latest version Discord RPC. You can do so by going to Discord's GitHub repository here. And download the latest discord-rpc-win.zip file, as of writing we'll be using v3.3.0. Once downloaded, open the archive and go to win32-dynamic/lib/ and extract "discord-rpc.lib" to your mod's <source code directory>/src/lib/public/ once that's done we'll do the same for the include files, in your archive take a step back and go into the include folder, now extract "discord_rpc.h" and "discord_register.h" to <source code directory>/src/public/. Now for the final step, once again in your archive take a step back and go into the bin folder, now extract "discord-rpc.dll" to your mod's /bin folder ex. sourcemods/YourMod/bin/.

The Setup

Now that the installation is done, it's time to setup our system. There's not a whole lot to do here, but this is a necessary step you'll have to take in order to make the implementation work.

To start, go to <source code directory>/src/vpc_scripts/ and open "source_dll_win32_base.vpc" with your favorite text editor, I recommend Notepad++. Once you've opened the file, scroll all the way down to $Folder "Link Libraries" and under $Implib "$LIBPUBLIC\vstdlib" add $Lib "$LIBPUBLIC\discord-rpc".

All that needs to be done now is to start the Discord RPC system when the modification launches. To do this, open up "cdll_client_int.cpp", and at the bottom add #include "discord_rpc.h" and #include <time.h> to the list of includes, then under

static ConVar s_cl_class("cl_class", "default", FCVAR_USERINFO|FCVAR_ARCHIVE, "Default class when joining a game");

add

// Discord RPC
static ConVar cl_discord_appid("cl_discord_appid", "123456789123456789", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT);
static int64_t startTimestamp = time(0);

where the numbers after "cl_discord_appid" is your applications RPC appid, to set up a RPC client go here, once your application is set up the number you'll want to place after "cl_discord_appid" is the "Client ID" seen in "General Information".

When that's done add the following above CHLClient::CHLClient()

//-----------------------------------------------------------------------------
// Discord RPC
//-----------------------------------------------------------------------------
static void HandleDiscordReady(const DiscordUser* connectedUser)
{
	DevMsg("Discord: Connected to user %s#%s - %s\n",
		connectedUser->username,
		connectedUser->discriminator,
		connectedUser->userId);
}

static void HandleDiscordDisconnected(int errcode, const char* message)
{
	DevMsg("Discord: Disconnected (%d: %s)\n", errcode, message);
}

static void HandleDiscordError(int errcode, const char* message)
{
	DevMsg("Discord: Error (%d: %s)\n", errcode, message);
}

static void HandleDiscordJoin(const char* secret)
{
	// Not implemented
}

static void HandleDiscordSpectate(const char* secret)
{
	// Not implemented
}

static void HandleDiscordJoinRequest(const DiscordUser* request)
{
	// Not implemented
}

Now scroll down to CHLClient::Init( ... ) and add the following above return true; at the bottom of the function.

// Discord RPC
DiscordEventHandlers handlers;
memset(&handlers, 0, sizeof(handlers));
	
handlers.ready = HandleDiscordReady;
handlers.disconnected = HandleDiscordDisconnected;
handlers.errored = HandleDiscordError;
handlers.joinGame = HandleDiscordJoin;
handlers.spectateGame = HandleDiscordSpectate;
handlers.joinRequest = HandleDiscordJoinRequest;

char appid[255];
sprintf(appid, "%d", engine->GetAppID());
Discord_Initialize(cl_discord_appid.GetString(), &handlers, 1, appid);

if (!g_bTextMode)
{
	DiscordRichPresence discordPresence;
	memset(&discordPresence, 0, sizeof(discordPresence));

	discordPresence.state = "In-Game";
	discordPresence.details = "Main Menu";
	discordPresence.startTimestamp = startTimestamp;
	discordPresence.largeImageKey = "ModImageHere";
	Discord_UpdatePresence(&discordPresence);
}

"ModImageHere" is an image key, remember that Discord developer page you were on earlier, where you set up your application? Go back there and to your application page, now go down to "Rich Presence" and click on "Art Assets" and click "Add Image(s)". Add an image you want for your mod and whatever you name that image is the name you replace "ModImageHere" with.

Now that that's done, let's go further down to the CHLClient::Shutdown( void ) function, here we'll add the Shutdown method, add the following below the "#endif" for "WORKSHOP_IMPORT_ENABLED"

// Discord RPC
Discord_Shutdown();

Now go further down to CHLClient::LevelInitPreEntity( ... ) and above the comment for g_RagdollLVManager.SetLowViolence( pMapName ); add

// Discord RPC
if (!g_bTextMode)
{
	DiscordRichPresence discordPresence;
	memset(&discordPresence, 0, sizeof(discordPresence));

	char buffer[256];
	discordPresence.state = "In-Game";
	sprintf(buffer, "Map: %s", pMapName);
	discordPresence.details = buffer;
	discordPresence.largeImageKey = "ModImageHere";
	Discord_UpdatePresence(&discordPresence);
}

Now we'll add the last piece of code into CHLClient::LevelShutdown( void ). Below

gHUD.LevelShutdown();

add

// Discord RPC
if (!g_bTextMode)
{
	DiscordRichPresence discordPresence;
	memset(&discordPresence, 0, sizeof(discordPresence));

	discordPresence.state = "In-Game";
	discordPresence.details = "Main Menu";
	discordPresence.startTimestamp = startTimestamp;
	discordPresence.largeImageKey = "ModImageHere";
	Discord_UpdatePresence(&discordPresence);
}

Conclusion

The installation and setup are both complete! And that's it, try launching your mod and check your Discord status, now load up a map and check again!

This system works for both the SP and MP branch of Source SDK 2013. Source SDK 2007 has not been tested yet, but should work just as well.