add discord rpc support

This commit is contained in:
Jan200101 2020-06-08 22:48:23 +02:00
parent a3ee774ba2
commit f1ba90d25b
No known key found for this signature in database
GPG key ID: 5B71B1D78B882E05
7 changed files with 431 additions and 3 deletions

View file

@ -44,6 +44,8 @@ EXT_OPTIONS_MENU ?= 1
TEXTSAVES ?= 0 TEXTSAVES ?= 0
# Load resources from external files # Load resources from external files
EXTERNAL_DATA ?= 0 EXTERNAL_DATA ?= 0
# Enable Discord Rich Presence
DISCORDRPC ?= 0
# Various workarounds for weird toolchains # Various workarounds for weird toolchains
@ -269,6 +271,10 @@ LEVEL_DIRS := $(patsubst levels/%,%,$(dir $(wildcard levels/*/header.h)))
SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller SRC_DIRS := src src/engine src/game src/audio src/menu src/buffers actors levels bin data assets src/pc src/pc/gfx src/pc/audio src/pc/controller
ASM_DIRS := ASM_DIRS :=
ifeq ($(DISCORDRPC),1)
SRC_DIRS += src/pc/discord
endif
BIN_DIRS := bin bin/$(VERSION) BIN_DIRS := bin bin/$(VERSION)
ULTRA_SRC_DIRS := lib/src lib/src/math ULTRA_SRC_DIRS := lib/src lib/src/math
@ -448,6 +454,18 @@ ULTRA_O_FILES := $(foreach file,$(ULTRA_S_FILES),$(BUILD_DIR)/$(file:.s=.o)) \
GODDARD_O_FILES := $(foreach file,$(GODDARD_C_FILES),$(BUILD_DIR)/$(file:.c=.o)) GODDARD_O_FILES := $(foreach file,$(GODDARD_C_FILES),$(BUILD_DIR)/$(file:.c=.o))
RPC_LIBS :=
ifeq ($(DISCORDRPC),1)
ifeq ($(WINDOWS_BUILD),1)
RPC_LIBS := src/pc/discord/libs/libdiscord-rpc-win.a
else ifeq ($(OSX_BUILD),1)
# needs testing
RPC_LIBS := src/pc/discord/libs/libdiscord-rpc-osx.a
else
RPC_LIBS := src/pc/discord/libs/libdiscord-rpc-unix.a
endif
endif
# Automatic dependency files # Automatic dependency files
DEP_FILES := $(O_FILES:.o=.d) $(ULTRA_O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(BUILD_DIR)/$(LD_SCRIPT).d DEP_FILES := $(O_FILES:.o=.d) $(ULTRA_O_FILES:.o=.d) $(GODDARD_O_FILES:.o=.d) $(BUILD_DIR)/$(LD_SCRIPT).d
@ -475,7 +493,9 @@ endif
LD := $(CC) LD := $(CC)
ifeq ($(WINDOWS_BUILD),1) ifeq ($(DISCORDRPC),1)
LD := $(CXX)
else ifeq ($(WINDOWS_BUILD),1)
ifeq ($(CROSS),i686-w64-mingw32.static-) # fixes compilation in MXE on Linux and WSL ifeq ($(CROSS),i686-w64-mingw32.static-) # fixes compilation in MXE on Linux and WSL
LD := $(CC) LD := $(CC)
else ifeq ($(CROSS),x86_64-w64-mingw32.static-) else ifeq ($(CROSS),x86_64-w64-mingw32.static-)
@ -536,6 +556,12 @@ ifeq ($(NODRAWINGDISTANCE),1)
CFLAGS += -DNODRAWINGDISTANCE CFLAGS += -DNODRAWINGDISTANCE
endif endif
# Check for Discord Rich Presence option
ifeq ($(DISCORDRPC),1)
CC_CHECK += -DDISCORDRPC
CFLAGS += -DDISCORDRPC
endif
# Check for texture fix option # Check for texture fix option
ifeq ($(TEXTURE_FIX),1) ifeq ($(TEXTURE_FIX),1)
CC_CHECK += -DTEXTURE_FIX CC_CHECK += -DTEXTURE_FIX
@ -737,11 +763,17 @@ $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD
$(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
ifeq ($(DISCORDRPC),1)
$(BUILD_DIR)/src/pc/discord/discordrpc.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
endif
else else
$(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h
ifeq ($(DISCORDRPC),1)
$(BUILD_DIR)/src/pc/discord/discordrpc.o: $(BUILD_DIR)/include/text_strings.h
endif
endif endif
################################################################ ################################################################
@ -924,8 +956,8 @@ $(BUILD_DIR)/%.o: %.s
$(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(RPC_LIBS)
$(LD) -L $(BUILD_DIR) -o $@ $(O_FILES) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS) $(LD) -L $(BUILD_DIR) -o $@ $(O_FILES) $(SOUND_OBJ_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(RPC_LIBS) $(LDFLAGS)
.PHONY: all clean distclean default diff test load libultra res .PHONY: all clean distclean default diff test load libultra res
.PRECIOUS: $(BUILD_DIR)/bin/%.elf $(SOUND_BIN_DIR)/%.ctl $(SOUND_BIN_DIR)/%.tbl $(SOUND_SAMPLE_TABLES) $(SOUND_BIN_DIR)/%.s $(BUILD_DIR)/% .PRECIOUS: $(BUILD_DIR)/bin/%.elf $(SOUND_BIN_DIR)/%.ctl $(SOUND_BIN_DIR)/%.tbl $(SOUND_SAMPLE_TABLES) $(SOUND_BIN_DIR)/%.s $(BUILD_DIR)/%

View file

@ -421,4 +421,8 @@ void render_game(void) {
D_8032CE74 = NULL; D_8032CE74 = NULL;
D_8032CE78 = 0; D_8032CE78 = 0;
#ifdef DISCORDRPC
discordUpdateRichPresence();
#endif
} }

View file

@ -85,6 +85,9 @@ bool configCameraMouse = false;
#endif #endif
unsigned int configSkipIntro = 0; unsigned int configSkipIntro = 0;
bool configHUD = true; bool configHUD = true;
#ifdef DISCORDRPC
bool configDiscordRPC = true;
#endif
static const struct ConfigOption options[] = { static const struct ConfigOption options[] = {
{.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configWindow.fullscreen}, {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configWindow.fullscreen},
@ -126,6 +129,9 @@ static const struct ConfigOption options[] = {
{.name = "bettercam_degrade", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraDegrade}, {.name = "bettercam_degrade", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraDegrade},
#endif #endif
{.name = "skip_intro", .type = CONFIG_TYPE_UINT, .uintValue = &configSkipIntro}, // Add this back! {.name = "skip_intro", .type = CONFIG_TYPE_UINT, .uintValue = &configSkipIntro}, // Add this back!
#ifdef DISCORDRPC
{.name = "discordrpc_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configDiscordRPC},
#endif
}; };
// Reads an entire line from a file (excluding the newline character) and returns an allocated string // Reads an entire line from a file (excluding the newline character) and returns an allocated string

View file

@ -52,6 +52,9 @@ extern bool configEnableCamera;
extern bool configCameraMouse; extern bool configCameraMouse;
#endif #endif
extern bool configHUD; extern bool configHUD;
#ifdef DISCORDRPC
extern bool configDiscordRPC;
#endif
void configfile_load(const char *filename); void configfile_load(const char *filename);
void configfile_save(const char *filename); void configfile_save(const char *filename);

289
src/pc/discord/discordrpc.c Normal file
View file

@ -0,0 +1,289 @@
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "PR/ultratypes.h"
#include "memory.h"
#include "pc/configfile.h"
#include "discordrpc.h"
#define DISCORD_APP_ID "709083908708237342"
#define DISCORD_UPDATE_RATE 5
time_t lastUpdatedTime;
DiscordRichPresence discordRichPresence;
extern s16 gCurrCourseNum;
extern s16 gCurrActNum;
s16 lastCourseNum = -1;
s16 lastActNum = -1;
extern u8 seg2_course_name_table[];
extern u8 seg2_act_name_table[];
#ifdef VERSION_EU
extern s32 gInGameLanguage;
#endif
char stage[188];
char act[188];
char smallImageKey[5];
char largeImageKey[5];
char charset[0xFF+1] = {
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 7
' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', // 15
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 23
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 31
'w', 'x', 'y', 'z', ' ', ' ', ' ', ' ', // 39
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 49
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 55
' ', ' ', ' ', ' ', ' ', ' ', '\'', ' ', // 63
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 71
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 79
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 87
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 95
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 103
' ', ' ', ' ', ' ', ' ', ' ', ' ', ',', // 111
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 119
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 127
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 135
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 143
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 151
' ', ' ', ' ', ' ', ' ', ' ', ' ', '-', // 159
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 167
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 175
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 183
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 192
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 199
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 207
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 215
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 223
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 231
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 239
' ', ' ', '!', ' ', ' ', ' ', ' ', ' ', // 247
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' // 255
};
void convertstring(const u8 *str, char* output)
{
s32 strPos = 0;
bool capitalizeChar = true;
while (str[strPos] != 0xFF)
{
if (str[strPos] < 0xFF)
{
output[strPos] = charset[str[strPos]];
// if the char is a letter we can capatalize it
if (capitalizeChar && 0x0A <= str[strPos] && str[strPos] <= 0x23)
{
output[strPos] -= ('a' - 'A');
capitalizeChar = false;
}
}
else output[strPos] = ' ';
switch (output[strPos]) // decide if the next character should be capitalized
{
case ' ':
if (str[strPos] != 158) printf(stdout, "Unknown Character (%i)\n", str[strPos]); // inform that an unknown char was found
case '-':
capitalizeChar = true;
break;
default:
capitalizeChar = false;
break;
}
strPos++;
}
output[strPos] = '\0';
}
void reverse(char s[])
{
int i, j;
char c;
for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
void itoa(int n, char s[])
{
int i, sign;
if (n < 0)
n = -n;
i = 0;
do {
s[i++] = n % 10 + '0';
} while ((n /= 10) > 0);
s[i] = '\0';
reverse(s);
}
void OnReady( const DiscordUser* user )
{
discordReset();
}
void InitializeDiscord()
{
DiscordEventHandlers handlers;
memset(&handlers, 0, sizeof(handlers));
handlers.ready = OnReady;
Discord_Initialize( DISCORD_APP_ID, &handlers, false, "" );
}
void SetDetails()
{
if (lastCourseNum != gCurrCourseNum)
{
// If we are in in Course 0 we are in the castle which doesn't have a string
if (gCurrCourseNum)
{
void **courseNameTbl;
#ifndef VERSION_EU
courseNameTbl = segmented_to_virtual(seg2_course_name_table);
#else
switch (gInGameLanguage) {
case LANGUAGE_ENGLISH:
courseNameTbl = segmented_to_virtual(course_name_table_eu_en);
break;
case LANGUAGE_FRENCH:
courseNameTbl = segmented_to_virtual(course_name_table_eu_fr);
break;
case LANGUAGE_GERMAN:
courseNameTbl = segmented_to_virtual(course_name_table_eu_de);
break;
}
#endif
u8 *courseName = segmented_to_virtual(courseNameTbl[gCurrCourseNum - 1]);
convertstring(&courseName[3], stage);
}
else strcpy(stage, "Peach's Castle");
lastCourseNum = gCurrCourseNum;
}
}
void SetState()
{
if (lastActNum != gCurrActNum || lastCourseNum != gCurrCourseNum)
{
// when exiting a stage the act doesn't get reset
if (gCurrActNum && gCurrCourseNum)
{
if (gCurrCourseNum < 19) // any stage over 19 is a special stage without acts
{
void **actNameTbl;
#ifndef VERSION_EU
actNameTbl = segmented_to_virtual(seg2_act_name_table);
#else
switch (gInGameLanguage) {
case LANGUAGE_ENGLISH:
actNameTbl = segmented_to_virtual(act_name_table_eu_en);
break;
case LANGUAGE_FRENCH:
actNameTbl = segmented_to_virtual(act_name_table_eu_fr);
break;
case LANGUAGE_GERMAN:
actNameTbl = segmented_to_virtual(act_name_table_eu_de);
break;
}
#endif
u8 *actName = actName = segmented_to_virtual(actNameTbl[(gCurrCourseNum - 1) * 6 + gCurrActNum - 1]);
convertstring(actName, act);
}
else
{
act[0] = '\0';
gCurrActNum = 0;
}
}
else act[0] = '\0';
lastActNum = gCurrActNum;
}
}
void SetLogo()
{
if (lastCourseNum)
{
itoa(lastCourseNum, largeImageKey);
}
else strcpy(largeImageKey, "0");
/*
if (lastActNum)
{
itoa(lastActNum, smallImageKey);
}
else smallImageKey[0] = '\0';
*/
discordRichPresence.largeImageKey = largeImageKey;
//discordRichPresence.largeImageText = "";
//discordRichPresence.smallImageKey = smallImageKey;
//discordRichPresence.smallImageText = "";
}
void discordUpdateRichPresence()
{
if (!configDiscordRPC) return;
if (time(NULL) < lastUpdatedTime + DISCORD_UPDATE_RATE) return;
lastUpdatedTime = time(NULL);
SetState();
SetDetails();
SetLogo();
Discord_UpdatePresence(&discordRichPresence);
}
void discordShutdown()
{
Discord_Shutdown();
};
void discordInit()
{
if (configDiscordRPC)
{
InitializeDiscord();
discordRichPresence.details = stage;
discordRichPresence.state = act;
lastUpdatedTime = 0;
}
};
void discordReset()
{
memset( &discordRichPresence, 0, sizeof( discordRichPresence ) );
SetState();
SetDetails();
SetLogo();
Discord_UpdatePresence( &discordRichPresence );
}

View file

@ -0,0 +1,88 @@
#ifndef DISCORDRPC_H
#define DISCORDRPC_H
#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
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);
void discordUpdateRichPresence();
void discordShutdown();
void discordInit();
void discordReset();
#endif // DISCORDRPC_H

View file

@ -194,6 +194,9 @@ static void gfx_sdl_init(void) {
static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) {
Uint32 t; Uint32 t;
#ifdef DISCORDRPC
discordInit();
#endif
while (1) { while (1) {
t = SDL_GetTicks(); t = SDL_GetTicks();
run_one_game_iter(); run_one_game_iter();
@ -258,6 +261,9 @@ static void gfx_sdl_handle_events(void) {
} }
break; break;
case SDL_QUIT: case SDL_QUIT:
#ifdef DISCORDRPC
discordShutdown();
#endif
game_exit(); game_exit();
break; break;
} }