[Discord]: Add rich presence (#760)

This commit is contained in:
Edo 2023-02-08 19:57:27 +00:00 committed by GitHub
parent 1f0dc46c22
commit 12f31f1d75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 253 additions and 18 deletions

6
.gitmodules vendored
View File

@ -35,3 +35,9 @@
path = deps/json path = deps/json
url = https://github.com/nlohmann/json.git url = https://github.com/nlohmann/json.git
branch = v3.11.2 branch = v3.11.2
[submodule "deps/discord-rpc"]
path = deps/discord-rpc
url = https://github.com/discord/discord-rpc.git
[submodule "deps/rapidjson"]
path = deps/rapidjson
url = https://github.com/Tencent/rapidjson.git

1
deps/discord-rpc vendored Submodule

@ -0,0 +1 @@
Subproject commit 963aa9f3e5ce81a4682c6ca3d136cddda614db33

40
deps/premake/discord-rpc.lua vendored Normal file
View File

@ -0,0 +1,40 @@
discordrpc = {
source = path.join(dependencies.basePath, "discord-rpc"),
}
function discordrpc.import()
links { "discord-rpc" }
discordrpc.includes()
end
function discordrpc.includes()
includedirs {
path.join(discordrpc.source, "include"),
}
end
function discordrpc.project()
project "discord-rpc"
language "C++"
cppdialect "C++17"
discordrpc.includes()
rapidjson.import();
files {
path.join(discordrpc.source, "src/*.h"),
path.join(discordrpc.source, "src/*.cpp"),
}
removefiles {
path.join(discordrpc.source, "src/dllmain.cpp"),
path.join(discordrpc.source, "src/*_linux.cpp"),
path.join(discordrpc.source, "src/*_unix.cpp"),
path.join(discordrpc.source, "src/*_osx.cpp"),
}
warnings "Off"
kind "StaticLib"
end
table.insert(dependencies, discordrpc)

19
deps/premake/rapidjson.lua vendored Normal file
View File

@ -0,0 +1,19 @@
rapidjson = {
source = path.join(dependencies.basePath, "rapidjson"),
}
function rapidjson.import()
rapidjson.includes()
end
function rapidjson.includes()
includedirs {
path.join(rapidjson.source, "include"),
}
end
function rapidjson.project()
end
table.insert(dependencies, rapidjson)

1
deps/rapidjson vendored Submodule

@ -0,0 +1 @@
Subproject commit 012be8528783cdbf4b7a9e64f78bd8f056b97e24

View File

@ -15,6 +15,7 @@
#include "Modules/Console.hpp" #include "Modules/Console.hpp"
#include "Modules/D3D9Ex.hpp" #include "Modules/D3D9Ex.hpp"
#include "Modules/Debug.hpp" #include "Modules/Debug.hpp"
#include "Modules/Discord.hpp"
#include "Modules/Discovery.hpp" #include "Modules/Discovery.hpp"
#include "Modules/Download.hpp" #include "Modules/Download.hpp"
#include "Modules/Elevators.hpp" #include "Modules/Elevators.hpp"
@ -114,6 +115,7 @@ namespace Components
Register(new D3D9Ex()); Register(new D3D9Ex());
Register(new Debug()); Register(new Debug());
Register(new Dedicated()); Register(new Dedicated());
Register(new Discord());
Register(new Discovery()); Register(new Discovery());
Register(new Download()); Register(new Download());
Register(new Elevators()); Register(new Elevators());

View File

@ -0,0 +1,124 @@
#include <STDInclude.hpp>
#include "Discord.hpp"
#include "Party.hpp"
#include <discord_rpc.h>
namespace Components
{
static DiscordRichPresence DiscordPresence;
bool Discord::Initialized_;
static void Ready([[maybe_unused]] const DiscordUser* request)
{
ZeroMemory(&DiscordPresence, sizeof(DiscordPresence));
DiscordPresence.instance = 1;
Logger::Print("Discord: Ready\n");
Discord_UpdatePresence(&DiscordPresence);
}
static void Errored(const int errorCode, const char* message)
{
Logger::Print(Game::CON_CHANNEL_ERROR, "Discord: Error (%i): %s\n", errorCode, message);
}
void Discord::UpdateDiscord()
{
Discord_RunCallbacks();
if (!Game::CL_IsCgameInitialized())
{
DiscordPresence.details = "Multiplayer";
DiscordPresence.state = "Main Menu";
DiscordPresence.largeImageKey = "menu_multiplayer";
DiscordPresence.partySize = 0;
DiscordPresence.partyMax = 0;
DiscordPresence.startTimestamp = 0;
Discord_UpdatePresence(&DiscordPresence);
return;
}
const auto* map = Game::UI_GetMapDisplayName((*Game::ui_mapname)->current.string);
const Game::StringTable* table;
Game::StringTable_GetAsset_FastFile("mp/gameTypesTable.csv", &table);
const auto row = Game::StringTable_LookupRowNumForValue(table, 0, (*Game::ui_gametype)->current.string);
if (row != -1)
{
const auto* value = Game::StringTable_GetColumnValueForRow(table, row, 1);
const auto* localize = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_LOCALIZE_ENTRY, value).localize;
DiscordPresence.details = Utils::String::Format("{} on {}", localize->value, map);
}
else
{
DiscordPresence.details = Utils::String::Format("Team Deathmatch on {}", map);
}
const auto* hostName = Game::cls->servername;
if (std::strcmp(hostName, "localhost") == 0)
{
DiscordPresence.state = "Private Match";
}
else
{
char hostNameBuffer[256]{};
TextRenderer::StripColors(Party::GetHostName().data(), hostNameBuffer, sizeof(hostNameBuffer));
TextRenderer::StripAllTextIcons(hostNameBuffer, hostNameBuffer, sizeof(hostNameBuffer));
DiscordPresence.state = hostNameBuffer;
}
DiscordPresence.partySize = 0;
DiscordPresence.partyMax = Party::GetMaxClients();
if (!DiscordPresence.startTimestamp)
{
DiscordPresence.startTimestamp = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
DiscordPresence.largeImageKey = "menu_multiplayer";
Discord_UpdatePresence(&DiscordPresence);
}
Discord::Discord()
{
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
{
return;
}
DiscordEventHandlers handlers;
ZeroMemory(&handlers, sizeof(handlers));
handlers.ready = Ready;
handlers.errored = Errored;
handlers.disconnected = Errored;
handlers.joinGame = nullptr;
handlers.spectateGame = nullptr;
handlers.joinRequest = nullptr;
Discord_Initialize("1072930169385394288", &handlers, 1, nullptr);
Scheduler::Once(UpdateDiscord, Scheduler::Pipeline::MAIN);
Scheduler::Loop(UpdateDiscord, Scheduler::Pipeline::MAIN, 15s);
Initialized_ = true;
}
void Discord::preDestroy()
{
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || !Initialized_)
{
return;
}
Discord_Shutdown();
}
}

View File

@ -0,0 +1,17 @@
#pragma once
namespace Components
{
class Discord : public Component
{
public:
Discord();
void preDestroy() override;
private:
static bool Initialized_;
static void UpdateDiscord();
};
}

View File

@ -103,6 +103,17 @@ namespace Components
return Container.motd; return Container.motd;
} }
std::string Party::GetHostName()
{
return Container.info.get("hostname");
}
int Party::GetMaxClients()
{
const auto value = Container.info.get("sv_maxclients");
return std::strtol(value.data(), nullptr, 10);
}
bool Party::PlaylistAwaiting() bool Party::PlaylistAwaiting()
{ {
return Container.awaitingPlaylist; return Container.awaitingPlaylist;

View File

@ -24,6 +24,8 @@ namespace Components
static bool IsEnabled(); static bool IsEnabled();
static std::string GetMotd(); static std::string GetMotd();
static std::string GetHostName();
static int GetMaxClients();
private: private:
static std::map<std::uint64_t, Network::Address> LobbyMap; static std::map<std::uint64_t, Network::Address> LobbyMap;

View File

@ -43,8 +43,6 @@ namespace Game
Svcmd_EntityList_f_t Svcmd_EntityList_f = Svcmd_EntityList_f_t(0x4B6A70); Svcmd_EntityList_f_t Svcmd_EntityList_f = Svcmd_EntityList_f_t(0x4B6A70);
GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = GScr_LoadGameTypeScript_t(0x4ED9A0);
Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader = Image_LoadFromFileWithReader_t(0x53ABF0); Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader = Image_LoadFromFileWithReader_t(0x53ABF0);
Image_Release_t Image_Release = Image_Release_t(0x51F010); Image_Release_t Image_Release = Image_Release_t(0x51F010);
@ -168,10 +166,6 @@ namespace Game
Steam_JoinLobby_t Steam_JoinLobby = Steam_JoinLobby_t(0x49CF70); Steam_JoinLobby_t Steam_JoinLobby = Steam_JoinLobby_t(0x49CF70);
StringTable_Lookup_t StringTable_Lookup = StringTable_Lookup_t(0x42F0E0);
StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow = StringTable_GetColumnValueForRow_t(0x4F2C80);
StringTable_HashString_t StringTable_HashString = StringTable_HashString_t(0x475EB0);
TeleportPlayer_t TeleportPlayer = TeleportPlayer_t(0x496850); TeleportPlayer_t TeleportPlayer = TeleportPlayer_t(0x496850);
UI_AddMenuList_t UI_AddMenuList = UI_AddMenuList_t(0x4533C0); UI_AddMenuList_t UI_AddMenuList = UI_AddMenuList_t(0x4533C0);
@ -191,6 +185,7 @@ namespace Game
UI_SafeTranslateString_t UI_SafeTranslateString = UI_SafeTranslateString_t(0x4F1700); UI_SafeTranslateString_t UI_SafeTranslateString = UI_SafeTranslateString_t(0x4F1700);
UI_ReplaceConversions_t UI_ReplaceConversions = UI_ReplaceConversions_t(0x4E9740); UI_ReplaceConversions_t UI_ReplaceConversions = UI_ReplaceConversions_t(0x4E9740);
UI_ParseInfos_t UI_ParseInfos = UI_ParseInfos_t(0x4027A0); UI_ParseInfos_t UI_ParseInfos = UI_ParseInfos_t(0x4027A0);
UI_GetMapDisplayName_t UI_GetMapDisplayName = UI_GetMapDisplayName_t(0x420700);
Win_GetLanguage_t Win_GetLanguage = Win_GetLanguage_t(0x45CBA0); Win_GetLanguage_t Win_GetLanguage = Win_GetLanguage_t(0x45CBA0);
@ -258,6 +253,12 @@ namespace Game
StructuredDataDef_GetAsset_t StructuredDataDef_GetAsset = StructuredDataDef_GetAsset_t(0x4D5C50); StructuredDataDef_GetAsset_t StructuredDataDef_GetAsset = StructuredDataDef_GetAsset_t(0x4D5C50);
StringTable_Lookup_t StringTable_Lookup = StringTable_Lookup_t(0x42F0E0);
StringTable_HashString_t StringTable_HashString = StringTable_HashString_t(0x475EB0);
StringTable_GetAsset_FastFile_t StringTable_GetAsset_FastFile = StringTable_GetAsset_FastFile_t(0x41A0B0);
StringTable_LookupRowNumForValue_t StringTable_LookupRowNumForValue = StringTable_LookupRowNumForValue_t(0x4AC180);
StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow = StringTable_GetColumnValueForRow_t(0x4F2C80);
longjmp_internal_t longjmp_internal = longjmp_internal_t(0x6B8898); longjmp_internal_t longjmp_internal = longjmp_internal_t(0x6B8898);
CmdArgs* cmd_args = reinterpret_cast<CmdArgs*>(0x1AAC5D0); CmdArgs* cmd_args = reinterpret_cast<CmdArgs*>(0x1AAC5D0);

View File

@ -84,9 +84,6 @@ namespace Game
typedef void(*Svcmd_EntityList_f_t)(); typedef void(*Svcmd_EntityList_f_t)();
extern Svcmd_EntityList_f_t Svcmd_EntityList_f; extern Svcmd_EntityList_f_t Svcmd_EntityList_f;
typedef void(*GScr_LoadGameTypeScript_t)();
extern GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript;
typedef int(*Reader_t)(char const*, int *); typedef int(*Reader_t)(char const*, int *);
typedef bool(*Image_LoadFromFileWithReader_t)(GfxImage* image, Reader_t reader); typedef bool(*Image_LoadFromFileWithReader_t)(GfxImage* image, Reader_t reader);
@ -206,6 +203,9 @@ namespace Game
typedef int(*UI_ParseInfos_t)(const char* buf, int max, char** infos); typedef int(*UI_ParseInfos_t)(const char* buf, int max, char** infos);
extern UI_ParseInfos_t UI_ParseInfos; extern UI_ParseInfos_t UI_ParseInfos;
typedef const char*(*UI_GetMapDisplayName_t)(const char* pszMap);
extern UI_GetMapDisplayName_t UI_GetMapDisplayName;
typedef void(*MSG_Init_t)(msg_t* buf, unsigned char* data, int length); typedef void(*MSG_Init_t)(msg_t* buf, unsigned char* data, int length);
extern MSG_Init_t MSG_Init; extern MSG_Init_t MSG_Init;
@ -407,15 +407,6 @@ namespace Game
typedef void(*Steam_JoinLobby_t)(SteamID, char); typedef void(*Steam_JoinLobby_t)(SteamID, char);
extern Steam_JoinLobby_t Steam_JoinLobby; extern Steam_JoinLobby_t Steam_JoinLobby;
typedef const char*(*StringTable_Lookup_t)(const StringTable *table, const int comparisonColumn, const char *value, const int valueColumn);
extern StringTable_Lookup_t StringTable_Lookup;
typedef const char* (*StringTable_GetColumnValueForRow_t)(const StringTable* table, int, int column);
extern StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow;
typedef int(*StringTable_HashString_t)(const char* string);
extern StringTable_HashString_t StringTable_HashString;
typedef void(*TeleportPlayer_t)(gentity_t* entity, float* pos, float* orientation); typedef void(*TeleportPlayer_t)(gentity_t* entity, float* pos, float* orientation);
extern TeleportPlayer_t TeleportPlayer; extern TeleportPlayer_t TeleportPlayer;
@ -590,6 +581,21 @@ namespace Game
typedef StructuredDataDef*(*StructuredDataDef_GetAsset_t)(const char* filename, unsigned int maxSize); typedef StructuredDataDef*(*StructuredDataDef_GetAsset_t)(const char* filename, unsigned int maxSize);
extern StructuredDataDef_GetAsset_t StructuredDataDef_GetAsset; extern StructuredDataDef_GetAsset_t StructuredDataDef_GetAsset;
typedef void(*StringTable_GetAsset_FastFile_t)(const char* filename, const StringTable** tablePtr);
extern StringTable_GetAsset_FastFile_t StringTable_GetAsset_FastFile;
typedef const char*(*StringTable_Lookup_t)(const StringTable* table, const int comparisonColumn, const char* value, const int valueColumn);
extern StringTable_Lookup_t StringTable_Lookup;
typedef int(*StringTable_HashString_t)(const char* string);
extern StringTable_HashString_t StringTable_HashString;
typedef int(*StringTable_LookupRowNumForValue_t)(const StringTable* table, int comparisonColumn, const char* value);
extern StringTable_LookupRowNumForValue_t StringTable_LookupRowNumForValue;
typedef const char*(*StringTable_GetColumnValueForRow_t)(const StringTable*, int row, int column);
extern StringTable_GetColumnValueForRow_t StringTable_GetColumnValueForRow;
typedef void(*longjmp_internal_t)(jmp_buf env, int status); typedef void(*longjmp_internal_t)(jmp_buf env, int status);
extern longjmp_internal_t longjmp_internal; extern longjmp_internal_t longjmp_internal;

View File

@ -69,6 +69,8 @@ namespace Game
Scr_FreeHudElemConstStrings_t Scr_FreeHudElemConstStrings = Scr_FreeHudElemConstStrings_t(0x5E1120); Scr_FreeHudElemConstStrings_t Scr_FreeHudElemConstStrings = Scr_FreeHudElemConstStrings_t(0x5E1120);
GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = GScr_LoadGameTypeScript_t(0x4ED9A0);
GetEntity_t GetEntity = GetEntity_t(0x4BC270); GetEntity_t GetEntity = GetEntity_t(0x4BC270);
GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0); GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0);

View File

@ -176,6 +176,9 @@ namespace Game
typedef void(*Scr_FreeHudElemConstStrings_t)(game_hudelem_s* hud); typedef void(*Scr_FreeHudElemConstStrings_t)(game_hudelem_s* hud);
extern Scr_FreeHudElemConstStrings_t Scr_FreeHudElemConstStrings; extern Scr_FreeHudElemConstStrings_t Scr_FreeHudElemConstStrings;
typedef void(*GScr_LoadGameTypeScript_t)();
extern GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript;
typedef gentity_s*(*GetPlayerEntity_t)(scr_entref_t entref); typedef gentity_s*(*GetPlayerEntity_t)(scr_entref_t entref);
extern GetPlayerEntity_t GetPlayerEntity; extern GetPlayerEntity_t GetPlayerEntity;