diff --git a/Minecraft.Client/Extrax64Stubs.cpp b/Minecraft.Client/Extrax64Stubs.cpp index da9fe12eb..60834c3ef 100644 --- a/Minecraft.Client/Extrax64Stubs.cpp +++ b/Minecraft.Client/Extrax64Stubs.cpp @@ -35,6 +35,9 @@ #include "Orbis\Sentient\DynamicConfigurations.h" #include #endif +#ifdef _WINDOWS64 +#include "Windows64/RichPresence/discord_rpc.h" +#endif #if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__) #ifdef _WINDOWS64 @@ -679,9 +682,109 @@ 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) +{ +} + +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; +} + +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"; + + // the comments are pretty generic, that would be sick if i could find the real ones + // i will probably make different icons depending of what situation the player is to make the rich presence more fun soon + 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_BLANK: + default: + s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_BLANK)); + discordPresence.smallImageKey = nullptr; + break; + } + + // After leaving for exemple an anvil the rich presence would get stuck at "Forging" so to avoid that behaviour, + // i just reset every (5*20 / 10) ticks (5 seconds), the division by ten is due to the fact that i call this function once per 10 ticks. + // If the player is still forging it will just reset itself and if he dont it will be corrected! + // + // \/ CODE TO CHANGE \/ + static int resetRP = 0; + if (++resetRP >= (5 * 20) / 10) + { + iVal = CONTEXT_GAME_STATE_BLANK; + s_stateUtf8 = WStringToUtf8(app.GetString(IDS_RICHPRESENCESTATE_BLANK)); + discordPresence.smallImageKey = nullptr; + } + + 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) {} diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index d97cbc383..05b31fcd9 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -1572,9 +1572,10 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" true $(OutDir)$(ProjectName).pdb - legacy_stdio_definitions.lib;d3d11.lib;d3dcompiler.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;%(AdditionalDependencies);XInput9_1_0.lib;wsock32.lib + legacy_stdio_definitions.lib;d3d11.lib;d3dcompiler.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;%(AdditionalDependencies);XInput9_1_0.lib;wsock32.lib;discord-rpc.lib;discord-rpc.lib NotSet false + Windows64\RichPresence\$(Configuration);Windows64\RichPresence;%(AdditionalLibraryDirectories) $(ProjectDir)xbox\xex-dev.xml @@ -1808,10 +1809,11 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CU true $(OutDir)$(ProjectName).pdb - legacy_stdio_definitions.lib;d3d11.lib;d3dcompiler.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;%(AdditionalDependencies) + legacy_stdio_definitions.lib;d3d11.lib;d3dcompiler.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;Windows64\RichPresence\discord-rpc.lib;%(AdditionalDependencies);discord-rpc.lib NotSet false UseLinkTimeCodeGeneration + Windows64\RichPresence\$(Configuration); Run postbuild script @@ -1947,9 +1949,10 @@ xcopy /q /y /i /s /e $(ProjectDir)DurangoMedia\CU $(LayoutDir)Image\Loose\CU true $(OutDir)$(ProjectName).pdb - d3d11.lib;d3dcompiler.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;%(AdditionalDependencies) + d3d11.lib;d3dcompiler.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;%(AdditionalDependencies);discord-rpc.lib NotSet false + Windows64\RichPresence\$(Configuration); $(ProjectDir)xbox\xex-dev.xml @@ -2798,10 +2801,11 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" false $(OutDir)default.pdb true - xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies);discord-rpc.lib xapilib.lib false false + Windows64\RichPresence\$(Configuration); $(ProjectDir)xbox\xex.xml @@ -2919,10 +2923,11 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" false $(OutDir)default.pdb true - xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies);discord-rpc.lib xapilib.lib false false + Windows64\RichPresence\$(Configuration); $(ProjectDir)xbox\xex.xml @@ -3040,10 +3045,11 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" false $(OutDir)default.pdb true - xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies);discord-rpc.lib xapilib.lib false false + Windows64\RichPresence\$(Configuration); $(ProjectDir)xbox\xex.xml @@ -3161,10 +3167,11 @@ if not exist "$(TargetDir)\savedata" mkdir "$(TargetDir)\savedata" false $(OutDir)default.pdb true - xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies) + xavatar2.lib;xapilib.lib;d3d9.lib;d3dx9.lib;xgraphics.lib;xboxkrnl.lib;xbox\Sentient\libs\SenCore.lib;xnet.lib;xaudio2.lib;xact3.lib;x3daudio.lib;xmcore.lib;vcomp.lib;xuirun.lib;xuirender.lib;xuihtml.lib;xonline.lib;xhv2.lib;qnetxaudio2.lib;xbox\4JLibs\libs\4J_Input.lib;xbox\4JLibs\libs\4J_Storage.lib;xbox\4JLibs\libs\4J_Profile.lib;xbox\4JLibs\libs\4J_Render.lib;..\Minecraft.World\ContentPackage\Minecraft.World.lib;xsocialpost.lib;xrnm.lib;xparty.lib;xbox\Sentient\libs\SenNews.lib;xbox\Sentient\libs\SenUGC.lib;xbox\Sentient\libs\SenBoxArt.lib;NuiApi.lib;ST.lib;NuiFitnessApi.lib;NuiHandles.lib;NuiSpeech.lib;NuiAudio.lib;xhttp.lib;xauth.lib;xgetserviceendpoint.lib;xav.lib;xjson.lib;%(AdditionalDependencies);discord-rpc.lib xapilib.lib false false + Windows64\RichPresence\$(Configuration); $(ProjectDir)xbox\xex.xml diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 46e8497a1..2d8585d42 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -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(); diff --git a/Minecraft.Client/Windows64/RichPresence/Debug/discord-rpc.lib b/Minecraft.Client/Windows64/RichPresence/Debug/discord-rpc.lib new file mode 100644 index 000000000..84ad3a482 Binary files /dev/null and b/Minecraft.Client/Windows64/RichPresence/Debug/discord-rpc.lib differ diff --git a/Minecraft.Client/Windows64/RichPresence/Release/discord-rpc.lib b/Minecraft.Client/Windows64/RichPresence/Release/discord-rpc.lib new file mode 100644 index 000000000..9851640d6 Binary files /dev/null and b/Minecraft.Client/Windows64/RichPresence/Release/discord-rpc.lib differ diff --git a/Minecraft.Client/Windows64/RichPresence/discord_rpc.h b/Minecraft.Client/Windows64/RichPresence/discord_rpc.h new file mode 100644 index 000000000..3e1441e05 --- /dev/null +++ b/Minecraft.Client/Windows64/RichPresence/discord_rpc.h @@ -0,0 +1,87 @@ +#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 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