Adding discord rich presence support for Windows64 (Early)

Cleaning PR

Finishing rich presence core and Cleaning

Removed Minecraft.Client/Minecraft.Client.vcxproj
This commit is contained in:
acth2 2026-03-08 20:06:40 +01:00
parent 3a5f1b6179
commit ef3c3e97e5
10 changed files with 269 additions and 48823 deletions

View file

@ -2795,9 +2795,6 @@ void ClientConnection::handleRespawn(shared_ptr<RespawnPacket> packet)
gameMode->getTutorial()->showTutorialPopup(false);
}
// 4J-JEV: Fix for Durango #156334 - Content: UI: Rich Presence 'In the Nether' message is updating with a 3 to 10 minute delay.
minecraft->localplayers[m_userIndex]->updateRichPresence();
ConnectionProgressParams *param = new ConnectionProgressParams();
param->iPad = m_userIndex;
if( packet->dimension == -1)

View file

@ -35,6 +35,9 @@
#include "Orbis\Sentient\DynamicConfigurations.h"
#include <perf.h>
#endif
#ifdef _WINDOWS64
#include "Windows64/RichPresence/discord_rpc.h"
#endif
#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__)
#ifdef _WINDOWS64
@ -679,9 +682,106 @@ eAwardType C_4JProfile::GetAwardType(int iAwardNumber) { return eAwardType_Ach
bool C_4JProfile::CanBeAwarded(int iQuadrant, int iAwardNumber) { return false; }
void C_4JProfile::Award(int iQuadrant, int iAwardNumber, bool bForce) {}
bool C_4JProfile::IsAwardsFlagSet(int iQuadrant, int iAward) { return false; }
void C_4JProfile::RichPresenceInit(int iPresenceCount, int iContextCount) {}
void C_4JProfile::RegisterRichPresenceContext(int iGameConfigContextID) {}
void C_4JProfile::SetRichPresenceContextValue(int iPad, int iContextID, int iVal) {}
void C_4JProfile::RichPresenceInit(int iPresenceCount, int iContextCount)
{
#ifdef _WINDOWS64
DiscordEventHandlers discordPresence;
memset(&discordPresence, 0, sizeof(discordPresence));
// 1480245069947732159 is on my discord account (acth2)
Discord_Initialize("1480245069947732159", &discordPresence, 1, NULL);
#endif
}
void C_4JProfile::RegisterRichPresenceContext(int iGameConfigContextID)
{
}
#ifdef _WINDOWS64
std::string WStringToUtf8(LPCWSTR wstr)
{
int size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
std::string result(size - 1, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, result.data(), size, NULL, NULL);
return result;
}
#endif
void C_4JProfile::SetRichPresenceContextValue(int iPad, int iContextID, int iVal)
{
#ifdef _WINDOWS64
DiscordRichPresence discordPresence;
memset(&discordPresence, 0, sizeof(discordPresence));
// std::string = UTF8
// LPCWSTR = UTF16
static std::string s_detailsUtf8;
static std::string s_stateUtf8;
// i cannot instantly do = because app.getString
s_detailsUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_BLANK));
if (iContextID == CONTEXT_GAME_STATE)
{
discordPresence.details = "Playing Minecraft: Legacy Console Edition";
discordPresence.largeImageKey = "main_icon";
discordPresence.largeImageText = "Minecraft: LCE";
switch (iVal)
{
case CONTEXT_GAME_STATE_NETHER:
s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_NETHER));
discordPresence.smallImageKey = "nether_icon";
break;
case CONTEXT_GAME_STATE_FORGING:
s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_FORGING));
discordPresence.smallImageKey = "anvil_icon";
break;
case CONTEXT_GAME_STATE_BOATING:
s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_BOATING));
discordPresence.smallImageKey = "boat_icon";
break;
case CONTEXT_GAME_STATE_CRAFTING:
s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_CRAFTING));
discordPresence.smallImageKey = "crafting_icon";
break;
case CONTEXT_GAME_STATE_RIDING_MINECART:
s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_RIDING_MINECART));
discordPresence.smallImageKey = "minecart_icon";
break;
case CONTEXT_GAME_STATE_RIDING_PIG:
s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_RIDING_PIG));
discordPresence.smallImageKey = "pig_icon";
break;
case CONTEXT_GAME_STATE_BREWING:
s_stateUtf8 = "Brewing a potion";
discordPresence.smallImageKey = "brewing_icon";
break;
case CONTEXT_GAME_STATE_ENCHANTING:
s_stateUtf8 = "Enchanting";
discordPresence.smallImageKey = "enchanting_icon";
break;
case CONTEXT_GAME_STATE_BLANK:
default:
s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_BLANK));
discordPresence.smallImageKey = nullptr;
break;
}
discordPresence.state = s_stateUtf8.c_str();
Discord_UpdatePresence(&discordPresence);
}
#endif
}
void C_4JProfile::SetCurrentGameActivity(int iPad, int iNewPresence, bool bSetOthersToIdle) {}
void C_4JProfile::DisplayFullVersionPurchase(bool bRequired, int iQuadrant, int iUpsellParam) {}
void C_4JProfile::SetUpsellCallback(void (*Func)(LPVOID lpParam, eUpsellType type, eUpsellResponse response, int iUserData), LPVOID lpParam) {}

View file

@ -3234,7 +3234,6 @@ void LevelRenderer::levelEvent(shared_ptr<Player> source, int type, int x, int y
level[playerIndex]->playStreamingMusic(L"", x, y, z); // 4J - used to pass NULL, but using empty string here now instead
}
}
mc->localplayers[playerIndex]->updateRichPresence();
}
break;
// 4J - new level event sounds brought forward from 1.2.3

View file

@ -33,6 +33,12 @@
#include "..\Minecraft.World\Random.h"
#include "..\Minecraft.World\TileEntity.h"
#include "..\Minecraft.World\Mth.h"
#include "..\AnvilMenu.h"
#include "..\CraftingMenu.h"
#include "..\BrewingStandMenu.h"
#include "..\EnchantmentMenu.h"
#include "AchievementPopup.h"
#include "CritParticle.h"
@ -470,42 +476,6 @@ void LocalPlayer::aiStep()
yd = 0.0f;
onGround = true;
}
// Check if the player is idle and the rich presence needs updated
if( !m_bIsIdle && InputManager.GetIdleSeconds( m_iPad ) > PLAYER_IDLE_TIME )
{
ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_IDLE,false);
m_bIsIdle = true;
}
else if ( m_bIsIdle && InputManager.GetIdleSeconds( m_iPad ) < PLAYER_IDLE_TIME )
{
// Are we offline or online, and how many players are there
if(g_NetworkManager.GetPlayerCount()>1)
{
// only do it for this player here - each player will run this code
if(g_NetworkManager.IsLocalGame())
{
ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false);
}
else
{
ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYER,false);
}
}
else
{
if(g_NetworkManager.IsLocalGame())
{
ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false);
}
else
{
ProfileManager.SetCurrentGameActivity(m_iPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false);
}
}
updateRichPresence();
m_bIsIdle = false;
}
}
void LocalPlayer::changeDimension(int i)
@ -1600,42 +1570,67 @@ bool LocalPlayer::handleMouseClick(int button)
void LocalPlayer::updateRichPresence()
{
if((m_iPad!=-1)/* && !ui.GetMenuDisplayed(m_iPad)*/ )
{
shared_ptr<ItemInstance> selectedItem = inventory->getSelected();
if(selectedItem != NULL && selectedItem->id == Item::fishingRod_Id)
if (m_iPad == -1)
{
return;
}
if (containerMenu != nullptr && containerMenu != inventoryMenu)
{
if (dynamic_cast<AnvilMenu *>(containerMenu) != nullptr)
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_FORGING);
}
else if (dynamic_cast<CraftingMenu *>(containerMenu) != nullptr)
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_CRAFTING);
}
else if (dynamic_cast<BrewingStandMenu*>(containerMenu) != nullptr)
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_FISHING);
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_BREWING);
}
else if(selectedItem != NULL && selectedItem->id == Item::map_Id)
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_MAP);
}
else if ( (riding != NULL) && riding->instanceof(eTYPE_MINECART) )
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_RIDING_MINECART);
}
else if ( (riding != NULL) && riding->instanceof(eTYPE_BOAT) )
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_BOATING);
}
else if ( (riding != NULL) && riding->instanceof(eTYPE_PIG) )
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_RIDING_PIG);
}
else if( this->dimension == -1 )
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_NETHER);
}
else if( minecraft->soundEngine->GetIsPlayingStreamingCDMusic() )
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_CD);
}
else
{
app.SetRichPresenceContext(m_iPad,CONTEXT_GAME_STATE_BLANK);
}
}
else if (dynamic_cast<EnchantmentMenu *>(containerMenu) != nullptr)
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_ENCHANTING);
}
return;
}
// INGAME ACTIVITY
shared_ptr<ItemInstance> selectedItem = inventory->getSelected();
if (selectedItem != NULL && selectedItem->id == Item::fishingRod_Id)
{
// TODO
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_FISHING);
}
else if (selectedItem != NULL && selectedItem->id == Item::map_Id)
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_MAP);
}
else if ((riding != NULL) && riding->instanceof(eTYPE_MINECART))
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_RIDING_MINECART);
}
else if ((riding != NULL) && riding->instanceof(eTYPE_BOAT))
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_BOATING);
}
else if ((riding != NULL) && riding->instanceof(eTYPE_PIG))
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_RIDING_PIG);
}
else if (this->dimension == -1)
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_NETHER);
}
else if (minecraft->soundEngine->GetIsPlayingStreamingCDMusic())
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_CD);
}
else
{
app.SetRichPresenceContext(m_iPad, CONTEXT_GAME_STATE_BLANK);
}
}
// 4J Stu - Added for telemetry

File diff suppressed because it is too large Load diff

View file

@ -425,6 +425,11 @@ void Minecraft::init()
progressRenderer = new ProgressRenderer(this);
RenderManager.CBuffLockStaticCreations();
// Setup discord rich presence
#ifdef _WINDOWS64
ProfileManager.RichPresenceInit(4, 1);
#endif
}
void Minecraft::renderLoadingScreen()
@ -2281,6 +2286,15 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
// Tick the opacity timer (to display the interface at default opacity for a certain time if the user has been navigating it)
app.TickOpacityTimer(iPad);
#ifdef _WINDOWS64
static int tickRP = 0;
if (++tickRP >= 10)
{
player->updateRichPresence();
tickRP = 0;
}
#endif
// 4J added
if( bFirst ) levelRenderer->destroyedTileManager->tick();
@ -3539,9 +3553,6 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures)
gameMode->getTutorial()->onSelectedItemChanged(player->inventory->getSelected());
}
// Update presence
player->updateRichPresence();
if (options->isFlying)
{
if (wheel > 0) wheel = 1;

View file

@ -366,8 +366,6 @@ void MultiplayerLocalPlayer::ride(shared_ptr<Entity> e)
TelemetryManager->RecordEnemyKilledOrOvercome(GetXboxPad(), 0, y, 0, 0, 0, 0, eventType);
}
updateRichPresence();
Minecraft *pMinecraft = Minecraft::GetInstance();
if( pMinecraft->localgameModes[m_iPad] != NULL )

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