Half-done Discord Rich Presence support

It's soooo close to being awesome, but so far away. Making game invitations as the host meant that it would redirect to "connect self" for other people, because we can't get the IP address from the host.

If anyone has a solution, you've got my gratitude; this was going so smoothly until it hit that brick wall.
This commit is contained in:
TehRealSalt 2018-10-22 00:34:45 -04:00
parent 2e3f6b09af
commit 4e549532ee
18 changed files with 400 additions and 5 deletions

Binary file not shown.

View file

@ -0,0 +1,26 @@
#pragma once
#if defined(DISCORD_DYNAMIC_LIB)
# if defined(_WIN32)
# if defined(DISCORD_BUILDING_SDK)
# define DISCORD_EXPORT __declspec(dllexport)
# else
# define DISCORD_EXPORT __declspec(dllimport)
# endif
# else
# define DISCORD_EXPORT __attribute__((visibility("default")))
# endif
#else
# define DISCORD_EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,87 @@
#pragma once
#include <stdint.h>
// clang-format off
#if defined(DISCORD_DYNAMIC_LIB)
# if defined(_WIN32)
# if defined(DISCORD_BUILDING_SDK)
# define DISCORD_EXPORT __declspec(dllexport)
# else
# define DISCORD_EXPORT __declspec(dllimport)
# endif
# else
# define DISCORD_EXPORT __attribute__((visibility("default")))
# endif
#else
# define DISCORD_EXPORT
#endif
// clang-format on
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DiscordRichPresence {
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
int64_t startTimestamp;
int64_t endTimestamp;
const char* largeImageKey; /* max 32 bytes */
const char* largeImageText; /* max 128 bytes */
const char* smallImageKey; /* max 32 bytes */
const char* smallImageText; /* max 128 bytes */
const char* partyId; /* max 128 bytes */
int partySize;
int partyMax;
const char* matchSecret; /* max 128 bytes */
const char* joinSecret; /* max 128 bytes */
const char* spectateSecret; /* max 128 bytes */
int8_t instance;
} DiscordRichPresence;
typedef struct DiscordUser {
const char* userId;
const char* username;
const char* discriminator;
const char* avatar;
} DiscordUser;
typedef struct DiscordEventHandlers {
void (*ready)(const DiscordUser* request);
void (*disconnected)(int errorCode, const char* message);
void (*errored)(int errorCode, const char* message);
void (*joinGame)(const char* joinSecret);
void (*spectateGame)(const char* spectateSecret);
void (*joinRequest)(const DiscordUser* request);
} DiscordEventHandlers;
#define DISCORD_REPLY_NO 0
#define DISCORD_REPLY_YES 1
#define DISCORD_REPLY_IGNORE 2
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId);
DISCORD_EXPORT void Discord_Shutdown(void);
/* checks for incoming messages, dispatches callbacks */
DISCORD_EXPORT void Discord_RunCallbacks(void);
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
#ifdef DISCORD_DISABLE_IO_THREAD
DISCORD_EXPORT void Discord_UpdateConnection(void);
#endif
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
DISCORD_EXPORT void Discord_ClearPresence(void);
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
#ifdef __cplusplus
} /* extern "C" */
#endif

Binary file not shown.

View file

@ -220,6 +220,8 @@ set(SRB2_CONFIG_HAVE_ZLIB ON CACHE BOOL
"Enable zlib support.") "Enable zlib support.")
set(SRB2_CONFIG_HAVE_GME ON CACHE BOOL set(SRB2_CONFIG_HAVE_GME ON CACHE BOOL
"Enable GME support.") "Enable GME support.")
set(SRB2_CONFIG_HAVE_DISCORDRPC OFF CACHE BOOL
"Enable Discord rich presence support.")
set(SRB2_CONFIG_HWRENDER ON CACHE BOOL set(SRB2_CONFIG_HWRENDER ON CACHE BOOL
"Enable hardware rendering through OpenGL.") "Enable hardware rendering through OpenGL.")
set(SRB2_CONFIG_USEASM OFF CACHE BOOL set(SRB2_CONFIG_USEASM OFF CACHE BOOL
@ -325,6 +327,19 @@ if(${SRB2_CONFIG_HAVE_GME})
endif() endif()
endif() endif()
# TO-DO: make this check for the dependency like the others..
if(${SRB2_CONFIG_HAVE_DISCORDRPC})
add_definitions(-DHAVE_DISCORDRPC)
set(SRB2_DISCORDRPC_SOURCES
discord.c
discord.h
)
prepend_sources(SRB2_DISCORDRPC_SOURCES)
source_group("Discord RPC" FILES ${SRB2_DISCORDRPC_SOURCES})
endif()
if(${SRB2_CONFIG_HAVE_ZLIB}) if(${SRB2_CONFIG_HAVE_ZLIB})
find_package(ZLIB) find_package(ZLIB)
if(${ZLIB_FOUND}) if(${ZLIB_FOUND})

View file

@ -367,6 +367,13 @@ CFLAGS+=-DHAVE_MINIUPNPC
endif endif
endif endif
ifdef HAVE_DISCORDRPC
LIBS+=-ldiscord-rpc
CFLAGS+=-DHAVE_DISCORDRPC
OBJS+=$(OBJDIR)/discord.o
OBJS+=$(OBJDIR)/discord_pass.o
endif
ifndef NO_LUA ifndef NO_LUA
include blua/Makefile.cfg include blua/Makefile.cfg
endif endif

View file

@ -55,6 +55,10 @@
#include "sdl12/SRB2XBOX/xboxhelp.h" #include "sdl12/SRB2XBOX/xboxhelp.h"
#endif #endif
#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif
// //
// NETWORKING // NETWORKING
// //
@ -3258,6 +3262,10 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
LUAh_PlayerJoin(newplayernum); LUAh_PlayerJoin(newplayernum);
#endif #endif
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
} }
// Xcmd XD_REMOVEPLAYER // Xcmd XD_REMOVEPLAYER
@ -3279,6 +3287,10 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
} }
CL_RemovePlayer(READUINT8(*p)); CL_RemovePlayer(READUINT8(*p));
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
} }
static boolean SV_AddWaitingPlayers(void) static boolean SV_AddWaitingPlayers(void)

View file

@ -102,6 +102,10 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "lua_script.h" #include "lua_script.h"
#endif #endif
#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif
// platform independant focus loss // platform independant focus loss
UINT8 window_notinfocus = false; UINT8 window_notinfocus = false;
@ -1351,6 +1355,10 @@ void D_SRB2Main(void)
CONS_Printf("ST_Init(): Init status bar.\n"); CONS_Printf("ST_Init(): Init status bar.\n");
ST_Init(); ST_Init();
#ifdef HAVE_DISCORDRPC
DRPC_Init();
#endif
if (M_CheckParm("-room")) if (M_CheckParm("-room"))
{ {
if (!M_IsNextParm()) if (!M_IsNextParm())

View file

@ -55,6 +55,10 @@
#define CV_RESTRICT 0 #define CV_RESTRICT 0
#endif #endif
#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif
// ------ // ------
// protos // protos
// ------ // ------
@ -1793,6 +1797,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
} }
else else
SetPlayerSkinByNum(playernum, skin); SetPlayerSkinByNum(playernum, skin);
#ifdef HAVE_DISCORDRPC
if (playernum == consoleplayer)
DRPC_UpdatePresence();
#endif
} }
void SendWeaponPref(void) void SendWeaponPref(void)
@ -2385,6 +2394,10 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
if (demorecording) // Okay, level loaded, character spawned and skinned, if (demorecording) // Okay, level loaded, character spawned and skinned,
G_BeginRecording(); // I AM NOW READY TO RECORD. G_BeginRecording(); // I AM NOW READY TO RECORD.
demo_start = true; demo_start = true;
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
} }
static void Command_Pause(void) static void Command_Pause(void)
@ -4281,6 +4294,10 @@ static void TimeLimit_OnChange(void)
} }
else if (netgame || multiplayer) else if (netgame || multiplayer)
CONS_Printf(M_GetText("Time limit disabled\n")); CONS_Printf(M_GetText("Time limit disabled\n"));
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
} }
/** Adjusts certain settings to match a changed gametype. /** Adjusts certain settings to match a changed gametype.

View file

@ -475,7 +475,7 @@ static boolean SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
char wadfilename[MAX_WADPATH]; char wadfilename[MAX_WADPATH];
if (cv_noticedownload.value) if (cv_noticedownload.value)
CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node)); CONS_Printf("Sending file \"%s\" to node %d (%s)\n", filename, node, I_GetNodeAddress(node, false));
// Find the last file in the list and set a pointer to its "next" field // Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist; q = &transfer[node].txlist;

170
src/discord.c Normal file
View file

@ -0,0 +1,170 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2018 by Sally "TehRealSalt" Cochenour.
// Copyright (C) 2012-2016 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file discord.h
/// \brief Discord Rich Presence handling
#ifdef HAVE_DISCORDRPC
#include "i_system.h"
#include "d_clisrv.h"
#include "d_netcmd.h"
#include "i_net.h"
#include "g_game.h"
#include "p_tick.h"
#include "m_menu.h" // gametype_cons_t
#include "r_things.h" // skins
#include "mserv.h" // ms_RoomId
#include "discord.h"
#include "discord_pass.h" // .gitignore'd file for volitile information; DO NOT push this info
#include "doomdef.h"
//
// DRPC_Handle's
//
static inline void DRPC_HandleReady(const DiscordUser *user)
{
CONS_Printf("Discord: connected to %s#%s - %s\n", user->username, user->discriminator, user->userId);
}
static inline void DRPC_HandleDisconnect(int err, const char *msg)
{
CONS_Printf("Discord: disconnected (%d: %s)\n", err, msg);
}
static inline void DRPC_HandleError(int err, const char *msg)
{
CONS_Printf("Discord: error (%d, %s)\n", err, msg);
}
static inline void DRPC_HandleJoin(const char *secret)
{
CONS_Printf("Discord: connecting to %s\n", secret);
COM_BufAddText(va("connect \"%s\"\n", secret));
}
//
// DRPC_Init: starting up the handles, call Discord_initalize
//
void DRPC_Init(void)
{
DiscordEventHandlers handlers;
memset(&handlers, 0, sizeof(handlers));
if (!discordappid)
return;
handlers.ready = DRPC_HandleReady;
handlers.disconnected = DRPC_HandleDisconnect;
handlers.errored = DRPC_HandleError;
handlers.joinGame = DRPC_HandleJoin;
Discord_Initialize(discordappid, &handlers, 1, NULL);
I_AddExitFunc(Discord_Shutdown);
DRPC_UpdatePresence();
}
//
// DRPC_UpdatePresence: Called whenever anything changes about server info
//
void DRPC_UpdatePresence(void)
{
char mapimg[8];
char mapname[48];
char charimg[21];
char charname[28];
DiscordRichPresence discordPresence;
memset(&discordPresence, 0, sizeof(discordPresence));
if (discordappid)
{
// Server info
if (netgame)
{
const char *address;
switch (ms_RoomId)
{
case -1: discordPresence.state = "Private"; break; // Private server
case 33: discordPresence.state = "Standard"; break;
case 28: discordPresence.state = "Casual"; break;
default: discordPresence.state = "???"; break; // How?
}
discordPresence.partyId = "1"; // We don't really have "party" IDs, so to make invites expire we just let it reset to 0 outside of servers
// Grab the host's IP for joining.
if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode, true)) != NULL)
{
discordPresence.joinSecret = address;
CONS_Printf("%s\n", address);
}
discordPresence.partySize = D_NumPlayers(); // Players in server
discordPresence.partyMax = cv_maxplayers.value; // Max players
}
else
discordPresence.state = "Offline";
// Gametype info
if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)
{
if (modeattacking)
discordPresence.details = "Record Attack";
else
discordPresence.details = gametype_cons_t[gametype].strvalue;
}
// Map info
if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
{
snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap));
strlwr(mapimg);
discordPresence.largeImageKey = mapimg; // Map image
if (mapheaderinfo[gamemap-1]->lvlttl[0] != '\0')
snprintf(mapname, 48, "Map: %s%s%s",
mapheaderinfo[gamemap-1]->lvlttl,
(strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->zonttl) : // SRB2kart
((mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE"),
(strlen(mapheaderinfo[gamemap-1]->actnum) > 0) ? va(" %s",mapheaderinfo[gamemap-1]->actnum) : "");
else
snprintf(mapname, 48, "???");
discordPresence.largeImageText = mapname; // Map name
//if (cv_timelimit.value)
//discordPresence.endTimestamp = levelstarttime + (cv_timelimit.value*60); // Time limit if applicable
}
else if (gamestate == GS_VOTING)
{
discordPresence.largeImageText = "Voting";
}
// Player info
if (playeringame[consoleplayer])
{
//discordPresence.startTimestamp = levelstarttime; // Time in level
if (!players[consoleplayer].spectator)
{
snprintf(charimg, 21, "char%s", skins[players[consoleplayer].skin].name);
discordPresence.smallImageKey = charimg; // Character image
snprintf(charname, 28, "Character: %s", skins[players[consoleplayer].skin].realname);
discordPresence.smallImageText = charname; // Character name
}
}
}
Discord_UpdatePresence(&discordPresence);
}
#endif

20
src/discord.h Normal file
View file

@ -0,0 +1,20 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2018 by Sally "TehRealSalt" Cochenour.
// Copyright (C) 2012-2016 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file discord.h
/// \brief Discord Rich Presence handling
#ifdef HAVE_DISCORDRPC
#include "discord_rpc.h"
void DRPC_Init(void);
void DRPC_UpdatePresence(void);
#endif

0
src/discord_pass.c Normal file
View file

3
src/discord_pass.h Normal file
View file

@ -0,0 +1,3 @@
// This file is .gitignore'd for a reason! This is very sensitive info!
// Do not push any change that makes this a valid app ID!
const char* discordappid = "503531144395096085";

View file

@ -47,6 +47,10 @@
#include "md5.h" // demo checksums #include "md5.h" // demo checksums
#include "k_kart.h" // SRB2kart #include "k_kart.h" // SRB2kart
#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif
gameaction_t gameaction; gameaction_t gameaction;
gamestate_t gamestate = GS_NULL; gamestate_t gamestate = GS_NULL;
UINT8 ultimatemode = false; UINT8 ultimatemode = false;
@ -2252,6 +2256,10 @@ void G_Ticker(boolean run)
if (spectatedelay4) if (spectatedelay4)
spectatedelay4--; spectatedelay4--;
} }
#ifdef HAVE_DISCORDRPC
Discord_RunCallbacks();
#endif
} }
// //
@ -6347,6 +6355,9 @@ boolean G_CheckDemoStatus(void)
void G_SetGamestate(gamestate_t newstate) void G_SetGamestate(gamestate_t newstate)
{ {
gamestate = newstate; gamestate = newstate;
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
} }
/* These functions handle the exitgame flag. Before, when the user /* These functions handle the exitgame flag. Before, when the user

View file

@ -97,6 +97,10 @@
#include "i_addrinfo.h" #include "i_addrinfo.h"
#ifdef HAVE_DISCORDRPC
#include "discord.h"
#endif
// ================================ DEFINITIONS =============================== // ================================ DEFINITIONS ===============================
#define PACKET_SIZE 1024 #define PACKET_SIZE 1024
@ -845,6 +849,9 @@ void RegisterServer(void)
MSOpenUDPSocket(); MSOpenUDPSocket();
// keep the TCP connection open until AddToMasterServer() is completed; // keep the TCP connection open until AddToMasterServer() is completed;
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
} }
static inline void SendPingToMasterServer(void) static inline void SendPingToMasterServer(void)
@ -896,7 +903,7 @@ void SendAskInfoViaMS(INT32 node, tic_t asktime)
// This must be called after calling MSOpenUDPSocket, due to the // This must be called after calling MSOpenUDPSocket, due to the
// static buffer. // static buffer.
address = I_GetNodeAddress(node); address = I_GetNodeAddress(node, false);
// no address? // no address?
if (!address) if (!address)
@ -946,6 +953,10 @@ void UnregisterServer(void)
CloseConnection(); CloseConnection();
MSCloseUDPSocket(); MSCloseUDPSocket();
MSLastPing = 0; MSLastPing = 0;
#ifdef HAVE_DISCORDRPC
DRPC_UpdatePresence();
#endif
} }
void MasterClient_Ticker(void) void MasterClient_Ticker(void)

View file

@ -2850,11 +2850,11 @@ void I_Error(const char *error, ...)
I_ShutdownGraphics(); I_ShutdownGraphics();
if (errorcount == 6) if (errorcount == 6)
I_ShutdownInput(); I_ShutdownInput();
if (errorcount == 7)
I_ShutdownSystem();
if (errorcount == 8) if (errorcount == 8)
SDL_Quit(); I_ShutdownSystem();
if (errorcount == 9) if (errorcount == 9)
SDL_Quit();
if (errorcount == 10)
{ {
M_SaveConfig(NULL); M_SaveConfig(NULL);
G_SaveGameData(false); G_SaveGameData(false);

View file

@ -25,6 +25,8 @@ endif
HAVE_MINIUPNPC=1 HAVE_MINIUPNPC=1
HAVE_DISCORDRPC=1
OPTS=-DSTDC_HEADERS OPTS=-DSTDC_HEADERS
ifndef GCC44 ifndef GCC44
@ -133,3 +135,9 @@ else
LDFLAGS+=-L../libs/miniupnpc/mingw32 LDFLAGS+=-L../libs/miniupnpc/mingw32
endif #MINGW64 endif #MINGW64
endif endif
ifdef HAVE_DISCORDRPC
CPPFLAGS+=-I../libs/discord-rpc/win32-dynamic/include
LDFLAGS+=-L../libs/discord-rpc/win32-dynamic/lib
LIBS+=-ldiscord-rpc
endif