From 3797a1e9efa9423c5512004c18860c283a54301b Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Mon, 8 Jan 2018 00:40:50 -0600 Subject: [PATCH 1/3] Add Discord rich presence This would work, were it not for some freak stack corruption caused by GCC not liking the Discord RPC library... --- Attorney_Online_remake.pro | 14 +++++++-- aoapplication.cpp | 4 +++ aoapplication.h | 2 ++ discord_rich_presence.cpp | 58 ++++++++++++++++++++++++++++++++++++++ discord_rich_presence.h | 21 ++++++++++++++ packet_distribution.cpp | 22 ++++++++++++--- 6 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 discord_rich_presence.cpp create mode 100644 discord_rich_presence.h diff --git a/Attorney_Online_remake.pro b/Attorney_Online_remake.pro index 1f4df76..40355c5 100644 --- a/Attorney_Online_remake.pro +++ b/Attorney_Online_remake.pro @@ -47,7 +47,8 @@ SOURCES += main.cpp\ aotextarea.cpp \ aolineedit.cpp \ aotextedit.cpp \ - aoevidencedisplay.cpp + aoevidencedisplay.cpp \ + discord_rich_presence.cpp HEADERS += lobby.h \ aoimage.h \ @@ -76,9 +77,16 @@ HEADERS += lobby.h \ aotextarea.h \ aolineedit.h \ aotextedit.h \ - aoevidencedisplay.h + aoevidencedisplay.h \ + discord_rich_presence.h -unix:LIBS += -L$$PWD -lbass +# 1. You need to get BASS and put the x86 bass DLL/headers in the project root folder +# AND the compilation output folder. If you want a static link, you'll probably +# need the .lib file too. MinGW-GCC is really finicky finding BASS, it seems. +# 2. You need to compile the Discord Rich Presence SDK separately and add the lib/headers +# in the same way as BASS. Discord RPC uses CMake, which does not play nicely with +# QMake, so this step must be manual. +unix:LIBS += -L$$PWD -lbass -ldiscord-rpc win32:LIBS += "$$PWD/bass.dll" android:LIBS += -L$$PWD\android\libs\armeabi-v7a\ -lbass diff --git a/aoapplication.cpp b/aoapplication.cpp index 4362902..e170c99 100644 --- a/aoapplication.cpp +++ b/aoapplication.cpp @@ -12,6 +12,7 @@ AOApplication::AOApplication(int &argc, char **argv) : QApplication(argc, argv) { net_manager = new NetworkManager(this); + discord = new AttorneyOnline::Discord(); QObject::connect(net_manager, SIGNAL(ms_connect_finished(bool, bool)), SLOT(ms_connect_finished(bool, bool))); } @@ -20,6 +21,7 @@ AOApplication::~AOApplication() { destruct_lobby(); destruct_courtroom(); + delete discord; } void AOApplication::construct_lobby() @@ -38,6 +40,8 @@ void AOApplication::construct_lobby() int y = (screenGeometry.height()-w_lobby->height()) / 2; w_lobby->move(x, y); + discord->state_lobby(); + w_lobby->show(); } diff --git a/aoapplication.h b/aoapplication.h index 30b16b5..146850d 100644 --- a/aoapplication.h +++ b/aoapplication.h @@ -3,6 +3,7 @@ #include "aopacket.h" #include "datatypes.h" +#include "discord_rich_presence.h" #include #include @@ -23,6 +24,7 @@ public: NetworkManager *net_manager; Lobby *w_lobby; Courtroom *w_courtroom; + AttorneyOnline::Discord *discord; bool lobby_constructed = false; bool courtroom_constructed = false; diff --git a/discord_rich_presence.cpp b/discord_rich_presence.cpp new file mode 100644 index 0000000..5d64c10 --- /dev/null +++ b/discord_rich_presence.cpp @@ -0,0 +1,58 @@ +#include "discord_rich_presence.h" + +#include + +#include + +namespace AttorneyOnline { + +Discord::Discord() +{ + DiscordEventHandlers handlers; + std::memset(&handlers, 0, sizeof(handlers)); + handlers = {}; + handlers.ready = [] { + qInfo() << "Discord RPC ready"; + }; + /* + handlers.disconnected = [](int errorCode, const char* message) { + qInfo() << "Discord RPC disconnected! " << message; + }; + handlers.errored = [](int errorCode, const char* message) { + qWarning() << "Discord RPC errored out! " << message; + }; + */ + qInfo() << "Are things working out all right?"; + Discord_Initialize(APPLICATION_ID, &handlers, 1, nullptr); +} + +Discord::~Discord() +{ + Discord_Shutdown(); +} + +void Discord::state_lobby() +{ + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.state = "In Lobby"; + presence.details = "Idle"; + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + Discord_UpdatePresence(&presence); +} + +void Discord::state_server(const char* name, const char* server_id) +{ + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.state = "In a Server"; + presence.details = name; + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + presence.matchSecret = server_id; +} + +} diff --git a/discord_rich_presence.h b/discord_rich_presence.h new file mode 100644 index 0000000..087fe8d --- /dev/null +++ b/discord_rich_presence.h @@ -0,0 +1,21 @@ +#ifndef DISCORD_RICH_PRESENCE_H +#define DISCORD_RICH_PRESENCE_H + +#include + +namespace AttorneyOnline { + +class Discord +{ +private: + const char* APPLICATION_ID = "399779271737868288"; +public: + Discord(); + ~Discord(); + + void state_lobby(); + void state_server(const char* name, const char* server_id); +}; + +} +#endif // DISCORD_RICH_PRESENCE_H diff --git a/packet_distribution.cpp b/packet_distribution.cpp index 4d74654..6e3232a 100644 --- a/packet_distribution.cpp +++ b/packet_distribution.cpp @@ -8,6 +8,7 @@ #include "debug_functions.h" #include +#include void AOApplication::ms_packet_received(AOPacket *p_packet) { @@ -226,15 +227,24 @@ void AOApplication::server_packet_received(AOPacket *p_packet) QString window_title = "Attorney Online 2"; int selected_server = w_lobby->get_selected_server(); + QString server_address = "", server_name = ""; if (w_lobby->public_servers_selected) { - if (selected_server >= 0 && selected_server < server_list.size()) - window_title += ": " + server_list.at(selected_server).name; + if (selected_server >= 0 && selected_server < server_list.size()) { + auto info = server_list.at(selected_server); + server_name = info.name; + server_address = info.ip + info.port; + window_title += ": " + server_name; + } } else { - if (selected_server >= 0 && selected_server < favorite_list.size()) - window_title += ": " + favorite_list.at(selected_server).name; + if (selected_server >= 0 && selected_server < favorite_list.size()) { + auto info = favorite_list.at(selected_server); + server_name = info.name; + server_address = info.ip + info.port; + window_title += ": " + server_name; + } } w_courtroom->set_window_title(window_title); @@ -251,6 +261,10 @@ void AOApplication::server_packet_received(AOPacket *p_packet) f_packet = new AOPacket("askchar2#%"); send_server_packet(f_packet); + + QCryptographicHash hash(QCryptographicHash::Algorithm::Sha256); + hash.addData(server_address.toUtf8()); + discord->state_server((const char*) server_name.toLocal8Bit().data(), (const char*) hash.result().toBase64()); } else if (header == "CI") { From dee8de29da7b72b5a1b2566311402933bacf007f Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Mon, 8 Jan 2018 01:13:56 -0600 Subject: [PATCH 2/3] Just switch to DLLs. --- Attorney_Online_remake.pro | 5 ++- discord-rpc.h | 84 ++++++++++++++++++++++++++++++++++++++ discord_rich_presence.cpp | 1 + 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 discord-rpc.h diff --git a/Attorney_Online_remake.pro b/Attorney_Online_remake.pro index 40355c5..1c5ba4e 100644 --- a/Attorney_Online_remake.pro +++ b/Attorney_Online_remake.pro @@ -78,7 +78,8 @@ HEADERS += lobby.h \ aolineedit.h \ aotextedit.h \ aoevidencedisplay.h \ - discord_rich_presence.h + discord_rich_presence.h \ + discord-rpc.h # 1. You need to get BASS and put the x86 bass DLL/headers in the project root folder # AND the compilation output folder. If you want a static link, you'll probably @@ -87,7 +88,7 @@ HEADERS += lobby.h \ # in the same way as BASS. Discord RPC uses CMake, which does not play nicely with # QMake, so this step must be manual. unix:LIBS += -L$$PWD -lbass -ldiscord-rpc -win32:LIBS += "$$PWD/bass.dll" +win32:LIBS += -L$$PWD "$$PWD/bass.dll" -ldiscord-rpc #"$$PWD/discord-rpc.dll" android:LIBS += -L$$PWD\android\libs\armeabi-v7a\ -lbass CONFIG += c++11 diff --git a/discord-rpc.h b/discord-rpc.h new file mode 100644 index 0000000..8c117ac --- /dev/null +++ b/discord-rpc.h @@ -0,0 +1,84 @@ +#pragma once +#include + +// 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 DiscordJoinRequest { + const char* userId; + const char* username; + const char* discriminator; + const char* avatar; +} DiscordJoinRequest; + +typedef struct DiscordEventHandlers { + void (*ready)(); + 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 DiscordJoinRequest* 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_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/discord_rich_presence.cpp b/discord_rich_presence.cpp index 5d64c10..f5f6c11 100644 --- a/discord_rich_presence.cpp +++ b/discord_rich_presence.cpp @@ -53,6 +53,7 @@ void Discord::state_server(const char* name, const char* server_id) presence.largeImageText = "Objection!"; presence.instance = 1; presence.matchSecret = server_id; + Discord_UpdatePresence(&presence); } } From 8a91dea6ce2fa88cdc720759f17d3ac4cf05070b Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Mon, 8 Jan 2018 13:33:39 -0600 Subject: [PATCH 3/3] Add spectate state; add "playing as character" state --- charselect.cpp | 4 +++ courtroom.cpp | 6 ++++ discord_rich_presence.cpp | 69 +++++++++++++++++++++++++++++++++------ discord_rich_presence.h | 7 +++- packet_distribution.cpp | 2 +- 5 files changed, 76 insertions(+), 12 deletions(-) diff --git a/charselect.cpp b/charselect.cpp index 1f76e0b..4e4bccb 100644 --- a/charselect.cpp +++ b/charselect.cpp @@ -151,8 +151,12 @@ void Courtroom::char_clicked(int n_char) } if (n_real_char == m_cid) + { enter_courtroom(m_cid); + } else + { ao_app->send_server_packet(new AOPacket("CC#" + QString::number(ao_app->s_pv) + "#" + QString::number(n_real_char) + "#" + get_hdid() + "#%")); + } } diff --git a/courtroom.cpp b/courtroom.cpp index a8b96eb..14d3cbb 100644 --- a/courtroom.cpp +++ b/courtroom.cpp @@ -660,9 +660,15 @@ void Courtroom::enter_courtroom(int p_cid) QString f_char; if (m_cid == -1) + { + ao_app->discord->state_spectate(); f_char = ""; + } else + { f_char = ao_app->get_char_name(char_list.at(m_cid).name); + ao_app->discord->state_character(f_char.toStdString()); + } current_char = f_char; diff --git a/discord_rich_presence.cpp b/discord_rich_presence.cpp index f5f6c11..bcc0d2a 100644 --- a/discord_rich_presence.cpp +++ b/discord_rich_presence.cpp @@ -1,6 +1,7 @@ #include "discord_rich_presence.h" #include +#include #include @@ -14,15 +15,13 @@ Discord::Discord() handlers.ready = [] { qInfo() << "Discord RPC ready"; }; - /* handlers.disconnected = [](int errorCode, const char* message) { qInfo() << "Discord RPC disconnected! " << message; }; handlers.errored = [](int errorCode, const char* message) { qWarning() << "Discord RPC errored out! " << message; }; - */ - qInfo() << "Are things working out all right?"; + qInfo() << "Initializing Discord RPC"; Discord_Initialize(APPLICATION_ID, &handlers, 1, nullptr); } @@ -35,24 +34,74 @@ void Discord::state_lobby() { DiscordRichPresence presence; std::memset(&presence, 0, sizeof(presence)); + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + presence.state = "In Lobby"; presence.details = "Idle"; - presence.largeImageKey = "ao2-logo"; - presence.largeImageText = "Objection!"; - presence.instance = 1; Discord_UpdatePresence(&presence); } -void Discord::state_server(const char* name, const char* server_id) +void Discord::state_server(std::string name, std::string server_id) { + qDebug() << "Discord RPC: Setting server state"; + DiscordRichPresence presence; std::memset(&presence, 0, sizeof(presence)); - presence.state = "In a Server"; - presence.details = name; presence.largeImageKey = "ao2-logo"; presence.largeImageText = "Objection!"; presence.instance = 1; - presence.matchSecret = server_id; + + auto timestamp = static_cast(std::time(nullptr)); + + presence.state = "In a Server"; + presence.details = name.c_str(); + presence.matchSecret = server_id.c_str(); + presence.startTimestamp = this->timestamp; + + this->server_id = server_id; + this->server_name = name; + this->timestamp = timestamp; + Discord_UpdatePresence(&presence); +} + +void Discord::state_character(std::string name) +{ + auto name_internal = QString(name.c_str()).toLower().replace(' ', '_').toStdString(); + auto name_friendly = QString(name.c_str()).replace('_', ' ').toStdString(); + const std::string playing_as = "Playing as " + name_friendly; + qDebug() << "Discord RPC: Setting character state (" << playing_as.c_str() << ")"; + + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + presence.details = this->server_name.c_str(); + presence.matchSecret = this->server_id.c_str(); + presence.startTimestamp = this->timestamp; + + presence.state = playing_as.c_str(); + presence.smallImageKey = name_internal.c_str(); + // presence.smallImageText = name_internal.c_str(); + Discord_UpdatePresence(&presence); +} + +void Discord::state_spectate() +{ + qDebug() << "Discord RPC: Setting specator state"; + + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + presence.details = this->server_name.c_str(); + presence.matchSecret = this->server_id.c_str(); + presence.startTimestamp = this->timestamp; + + presence.state = "Spectating"; Discord_UpdatePresence(&presence); } diff --git a/discord_rich_presence.h b/discord_rich_presence.h index 087fe8d..3c9f2bd 100644 --- a/discord_rich_presence.h +++ b/discord_rich_presence.h @@ -1,6 +1,7 @@ #ifndef DISCORD_RICH_PRESENCE_H #define DISCORD_RICH_PRESENCE_H +#include #include namespace AttorneyOnline { @@ -9,12 +10,16 @@ class Discord { private: const char* APPLICATION_ID = "399779271737868288"; + std::string server_name, server_id; + int64_t timestamp; public: Discord(); ~Discord(); void state_lobby(); - void state_server(const char* name, const char* server_id); + void state_server(std::string name, std::string server_id); + void state_character(std::string name); + void state_spectate(); }; } diff --git a/packet_distribution.cpp b/packet_distribution.cpp index 6e3232a..3908ffa 100644 --- a/packet_distribution.cpp +++ b/packet_distribution.cpp @@ -264,7 +264,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet) QCryptographicHash hash(QCryptographicHash::Algorithm::Sha256); hash.addData(server_address.toUtf8()); - discord->state_server((const char*) server_name.toLocal8Bit().data(), (const char*) hash.result().toBase64()); + discord->state_server(server_name.toStdString(), hash.result().toBase64().toStdString()); } else if (header == "CI") {