From d9e1a1ebaf6702ba14470ed7e7037b915c46d3b0 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Tue, 19 Dec 2023 14:06:17 -0600 Subject: [PATCH] Added Discord Rich Presence integration. Added discord exclusion preprocessor checks in emscripten build. Changed emscripten callbacks to use the browser window instead of the internal body. --- .gitignore | 1 + Crawler/Crawler.cpp | 58 +- Crawler/Crawler.h | 3 + Crawler/Crawler.vcxproj | 59 +- Crawler/Crawler.vcxproj.filters | 99 ++ Crawler/Menu.h | 1 + Crawler/State_OverworldMap.cpp | 2 + Crawler/Version.h | 2 +- Crawler/assets/config/configuration.txt | 2 + Crawler/assets/heart_512.png | Bin 0 -> 6327 bytes Crawler/assets/nico-Ranger_512.png | Bin 0 -> 3388 bytes Crawler/assets/nico-Thief_512.png | Bin 0 -> 2999 bytes Crawler/assets/nico-Trapper_512.png | Bin 0 -> 2999 bytes Crawler/assets/nico-Warrior_512.png | Bin 0 -> 2999 bytes Crawler/assets/nico-Witch_512.png | Bin 0 -> 2999 bytes Crawler/assets/nico-Wizard_512.png | Bin 0 -> 6869 bytes Crawler/discord-files/achievement_manager.cpp | 99 ++ Crawler/discord-files/achievement_manager.h | 34 + Crawler/discord-files/activity_manager.cpp | 177 +++ Crawler/discord-files/activity_manager.h | 42 + Crawler/discord-files/application_manager.cpp | 78 ++ Crawler/discord-files/application_manager.h | 30 + Crawler/discord-files/core.cpp | 182 +++ Crawler/discord-files/core.h | 64 + Crawler/discord-files/discord.h | 16 + Crawler/discord-files/event.h | 59 + Crawler/discord-files/ffi.h | 1113 +++++++++++++++++ Crawler/discord-files/image_manager.cpp | 57 + Crawler/discord-files/image_manager.h | 28 + Crawler/discord-files/lobby_manager.cpp | 554 ++++++++ Crawler/discord-files/lobby_manager.h | 88 ++ Crawler/discord-files/network_manager.cpp | 103 ++ Crawler/discord-files/network_manager.h | 63 + Crawler/discord-files/overlay_manager.cpp | 229 ++++ Crawler/discord-files/overlay_manager.h | 57 + .../discord-files/relationship_manager.cpp | 91 ++ Crawler/discord-files/relationship_manager.h | 32 + Crawler/discord-files/storage_manager.cpp | 158 +++ Crawler/discord-files/storage_manager.h | 46 + Crawler/discord-files/store_manager.cpp | 162 +++ Crawler/discord-files/store_manager.h | 38 + Crawler/discord-files/types.cpp | 879 +++++++++++++ Crawler/discord-files/types.h | 567 +++++++++ Crawler/discord-files/user_manager.cpp | 80 ++ Crawler/discord-files/user_manager.h | 31 + Crawler/discord-files/voice_manager.cpp | 124 ++ Crawler/discord-files/voice_manager.h | 37 + Crawler/discord_game_sdk.bundle | Bin 0 -> 4251528 bytes Crawler/discord_game_sdk.dll | Bin 0 -> 3488768 bytes Crawler/discord_game_sdk.dll.lib | Bin 0 -> 2352 bytes Crawler/discord_game_sdk.dylib | Bin 0 -> 4251528 bytes Crawler/discord_game_sdk.so | Bin 0 -> 8469240 bytes Crawler/emscripten_build.ps1 | 2 +- Crawler/loc.sh | 4 +- Crawler/olcPixelGameEngine.h | 16 +- Crawler/pge.data | Bin 9350119 -> 10993973 bytes Crawler/pge.js | 2 +- Crawler/pge.wasm | Bin 1583311 -> 1998642 bytes x64/Release/Crawler.exe | Bin 1358848 -> 1598976 bytes 59 files changed, 5541 insertions(+), 28 deletions(-) create mode 100644 Crawler/assets/heart_512.png create mode 100644 Crawler/assets/nico-Ranger_512.png create mode 100644 Crawler/assets/nico-Thief_512.png create mode 100644 Crawler/assets/nico-Trapper_512.png create mode 100644 Crawler/assets/nico-Warrior_512.png create mode 100644 Crawler/assets/nico-Witch_512.png create mode 100644 Crawler/assets/nico-Wizard_512.png create mode 100644 Crawler/discord-files/achievement_manager.cpp create mode 100644 Crawler/discord-files/achievement_manager.h create mode 100644 Crawler/discord-files/activity_manager.cpp create mode 100644 Crawler/discord-files/activity_manager.h create mode 100644 Crawler/discord-files/application_manager.cpp create mode 100644 Crawler/discord-files/application_manager.h create mode 100644 Crawler/discord-files/core.cpp create mode 100644 Crawler/discord-files/core.h create mode 100644 Crawler/discord-files/discord.h create mode 100644 Crawler/discord-files/event.h create mode 100644 Crawler/discord-files/ffi.h create mode 100644 Crawler/discord-files/image_manager.cpp create mode 100644 Crawler/discord-files/image_manager.h create mode 100644 Crawler/discord-files/lobby_manager.cpp create mode 100644 Crawler/discord-files/lobby_manager.h create mode 100644 Crawler/discord-files/network_manager.cpp create mode 100644 Crawler/discord-files/network_manager.h create mode 100644 Crawler/discord-files/overlay_manager.cpp create mode 100644 Crawler/discord-files/overlay_manager.h create mode 100644 Crawler/discord-files/relationship_manager.cpp create mode 100644 Crawler/discord-files/relationship_manager.h create mode 100644 Crawler/discord-files/storage_manager.cpp create mode 100644 Crawler/discord-files/storage_manager.h create mode 100644 Crawler/discord-files/store_manager.cpp create mode 100644 Crawler/discord-files/store_manager.h create mode 100644 Crawler/discord-files/types.cpp create mode 100644 Crawler/discord-files/types.h create mode 100644 Crawler/discord-files/user_manager.cpp create mode 100644 Crawler/discord-files/user_manager.h create mode 100644 Crawler/discord-files/voice_manager.cpp create mode 100644 Crawler/discord-files/voice_manager.h create mode 100644 Crawler/discord_game_sdk.bundle create mode 100644 Crawler/discord_game_sdk.dll create mode 100644 Crawler/discord_game_sdk.dll.lib create mode 100644 Crawler/discord_game_sdk.dylib create mode 100644 Crawler/discord_game_sdk.so diff --git a/.gitignore b/.gitignore index e2b99c4e..0a633322 100644 --- a/.gitignore +++ b/.gitignore @@ -375,3 +375,4 @@ Crawler/Crawler.wasm Crawler/pixelGameEngine_wasm.o *.tiled-session /Crawler/Crawler.tiled-session +/Crawler/assets/discordAPI.txt diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index 921169fb..6f69c247 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -67,6 +67,9 @@ All rights reserved. #include "olcPGEX_TTF.h" #include "MenuItemItemButton.h" #include "Merchant.h" +#ifndef __EMSCRIPTEN__ +#include "discord-files/discord.h" +#endif INCLUDE_EMITTER_LIST @@ -89,14 +92,14 @@ InputGroup Crawler::KEY_DOWN; InputGroup Crawler::KEY_ATTACK; InputGroup Crawler::KEY_CONFIRM; InputGroup Crawler::KEY_MENU; +#ifndef __EMSCRIPTEN__ + ::discord::Core*Discord{}; +#endif float Crawler::SIZE_CHANGE_SPEED=1; Crawler::Crawler() { - sAppName = "Crawler Concept"; - game=this; - utils::datafile::Read(DATA,"assets/config/configuration.txt"); _DEBUG_MAP_LOAD_INFO=bool("debug_map_load_info"_I); @@ -141,6 +144,9 @@ Crawler::Crawler() } utils::datafile::DEBUG_ACCESS_OPTIONS="debug_access_options"_I; + + sAppName = "GAME_NAME"_S; + game=this; } bool Crawler::OnUserCreate(){ @@ -202,6 +208,8 @@ bool Crawler::OnUserCreate(){ utils::datafile::INITIAL_SETUP_COMPLETE=true; ValidateGameStatus(); //Checks to make sure everything has been initialized properly. + + SetupDiscord(); return true; } @@ -216,6 +224,11 @@ bool Crawler::OnUserUpdate(float fElapsedTime){ RenderMenu(); RenderFadeout(); RenderVersionInfo(); + #ifndef __EMSCRIPTEN__ + if(Discord){ + Discord->RunCallbacks(); + } + #endif return !gameEnd; } @@ -2276,4 +2289,43 @@ bool Crawler::GamePaused(){ void Crawler::EndGame(){ gameEnd=true; +} + +void Crawler::SetupDiscord(){ + gameStarted=time(NULL); + #ifndef __EMSCRIPTEN__ + auto result = ::discord::Core::Create(1186719371750555780,DiscordCreateFlags_NoRequireDiscord,&Discord); + if(result==::discord::Result::Ok){ + Discord->SetLogHook( + discord::LogLevel::Debug, [](discord::LogLevel level, const char* message) { + std::cerr << "Log(" << static_cast(level) << "): " << message << "\n"; + }); + std::cout<<"Connected to Discord!"<GetClassName()); + } + #endif +} + +void Crawler::UpdateDiscordStatus(std::string levelName,std::string className){ + #ifndef __EMSCRIPTEN__ + if(Discord){ + ::discord::Activity newActivity{}; + newActivity.SetDetails(levelName.c_str()); + newActivity.SetState(("Class: "+className).c_str()); + discord::ActivityTimestamps×tamps=newActivity.GetTimestamps(); + timestamps.SetStart(gameStarted); + newActivity.SetType(discord::ActivityType::Playing); + discord::ActivityAssets&assets=newActivity.GetAssets(); + assets.SetLargeImage("heart_512"); + if(levelName!="Main Menu"){ + std::for_each(className.begin(),className.end(),[](char&c){c=std::tolower(c);}); + assets.SetSmallImage(("nico-"+className+"_512").c_str()); + } + assets.SetLargeText(game->sAppName.c_str()); + assets.SetSmallText(className.c_str()); + Discord->ActivityManager().UpdateActivity(newActivity,[](::discord::Result result){ + std::cout<<"Discord Activity successfully updated!"<monstersToBeSpawned; + time_t gameStarted; void ValidateGameStatus(); + void SetupDiscord(); public: Crawler(); bool OnUserCreate() override; @@ -197,6 +199,7 @@ public: void RenderFadeout(); bool GamePaused(); void EndGame(); + void UpdateDiscordStatus(std::string levelName,std::string className); struct TileGroupData{ vi2d tilePos; diff --git a/Crawler/Crawler.vcxproj b/Crawler/Crawler.vcxproj index 93ab2218..7096085d 100644 --- a/Crawler/Crawler.vcxproj +++ b/Crawler/Crawler.vcxproj @@ -131,14 +131,14 @@ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 - C:\Users\sigon\Documents\include;%(AdditionalIncludeDirectories) + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files;C:\Users\sigon\Documents\include;%(AdditionalIncludeDirectories) /MP8 %(AdditionalOptions) Console true C:\Users\sigon\source\repos\Crawler\Crawler;%(AdditionalLibraryDirectories) - freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + discord_game_sdk.dll.lib;freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies); powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File update_version.ps1 "./version.h" @@ -154,14 +154,14 @@ true stdcpp20 /MP8 %(AdditionalOptions) - C:\Users\sigon\Documents\include; + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files;C:\Users\sigon\Documents\include; Console true true true - freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies);discord_game_sdk.dll.lib powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File update_version.ps1 "./version.h" @@ -177,14 +177,14 @@ true stdcpp20 /MP8 %(AdditionalOptions) - C:\Users\sigon\Documents\include; + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files;C:\Users\sigon\Documents\include;C:\Users\sigon\source\repos\Crawler\Crawler\discord-files Console true true true - freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + discord_game_sdk.dll.lib;freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies);discord_game_sdk.dll.lib powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File update_version.ps1 "./version.h" @@ -197,14 +197,14 @@ _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 - C:\Users\sigon\Documents\include;%(AdditionalIncludeDirectories) + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files;C:\Users\sigon\Documents\include;%(AdditionalIncludeDirectories) /MP8 %(AdditionalOptions) Console true C:\Users\sigon\source\repos\Crawler\Crawler;%(AdditionalLibraryDirectories) - freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + discord_game_sdk.dll.lib;freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies); powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File update_version.ps1 "./version.h" @@ -224,14 +224,14 @@ true stdcpp20 /MP8 %(AdditionalOptions) - C:\Users\sigon\Documents\include; + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files;C:\Users\sigon\Documents\include; Console true true true - freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies);discord_game_sdk.dll.lib powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File update_version.ps1 "./version.h" @@ -250,14 +250,14 @@ true stdcpp20 /MP8 %(AdditionalOptions) - C:\Users\sigon\Documents\include; + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files;C:\Users\sigon\Documents\include;C:\Users\sigon\source\repos\Crawler\Crawler\discord-files Console true true true - freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + discord_game_sdk.dll.lib;freetype.lib;$(CoreLibraryDependencies);%(AdditionalDependencies);discord_game_sdk.dll.lib powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File update_version.ps1 "./version.h" @@ -270,17 +270,21 @@ Console + $(CoreLibraryDependencies);%(AdditionalDependencies);discord_game_sdk.dll.lib stdcpp20 + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files; Console + $(CoreLibraryDependencies);%(AdditionalDependencies);discord_game_sdk.dll.lib stdcpp20 + C:\Users\sigon\source\repos\Crawler\Crawler\discord-files; @@ -307,6 +311,23 @@ + + + + + + + + + + + + + + + + + @@ -428,6 +449,20 @@ + + + + + + + + + + + + + + diff --git a/Crawler/Crawler.vcxproj.filters b/Crawler/Crawler.vcxproj.filters index 8ba3bc07..d3b2877b 100644 --- a/Crawler/Crawler.vcxproj.filters +++ b/Crawler/Crawler.vcxproj.filters @@ -73,6 +73,12 @@ {9ab34e07-c7ba-4f4b-9dad-29c7602c1550} + + {4fada79e-3b17-42df-8609-adf564fe12d9} + + + {8911e0bb-703c-4a2b-b3dd-259192956733} + @@ -312,6 +318,57 @@ Header Files + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + + + Header Files\discord-files + @@ -506,6 +563,48 @@ Source Files + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + + + Source Files\discord-files + diff --git a/Crawler/Menu.h b/Crawler/Menu.h index 4cdbfcf9..ee6be985 100644 --- a/Crawler/Menu.h +++ b/Crawler/Menu.h @@ -237,6 +237,7 @@ T*Component(MenuType menu,std::string componentName){ }else{ ERR("WARNING! Attempting to cast a button that isn't a "<(OVERWORLD_LEVEL_SELECT,"Spawns List")->UpdateSpawns(currentConnectionPoint->spawns); Component(OVERWORLD_LEVEL_SELECT,"Enter Button")->Enable(currentConnectionPoint->levelDataExists); Menu::OpenMenu(OVERWORLD_LEVEL_SELECT,false); + game->UpdateDiscordStatus("Overworld Map",game->GetPlayer()->GetClassName()); }; void State_OverworldMap::OnUserUpdate(Crawler*game){ if(Menu::stack.size()>1)return; @@ -156,6 +157,7 @@ ConnectionPoint&State_OverworldMap::GetCurrentConnectionPoint(){ } void State_OverworldMap::StartLevel(){ + game->UpdateDiscordStatus(State_OverworldMap::GetCurrentConnectionPoint().name,game->GetPlayer()->GetClassName()); if(State_OverworldMap::GetCurrentConnectionPoint().map.starts_with("STORY")){ VisualNovel::LoadVisualNovel(State_OverworldMap::GetCurrentConnectionPoint().map); }else{ diff --git a/Crawler/Version.h b/Crawler/Version.h index 58ca8324..3c78c82c 100644 --- a/Crawler/Version.h +++ b/Crawler/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 0 #define VERSION_MINOR 2 #define VERSION_PATCH 1 -#define VERSION_BUILD 4156 +#define VERSION_BUILD 4187 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Crawler/assets/config/configuration.txt b/Crawler/assets/config/configuration.txt index e9b9ab57..84a7fc9e 100644 --- a/Crawler/assets/config/configuration.txt +++ b/Crawler/assets/config/configuration.txt @@ -3,6 +3,8 @@ config_path = assets/config/ # 360x240 is 15x10 tiles of visibility. WINDOW_SIZE = 360,240 +GAME_NAME = Crawler Project + # Graphics Loading Config gfx_config = gfx/gfx.txt diff --git a/Crawler/assets/heart_512.png b/Crawler/assets/heart_512.png new file mode 100644 index 0000000000000000000000000000000000000000..db6bb0dd91b888fa47c70d37504a7283d15cb648 GIT binary patch literal 6327 zcmeHLdsGu=79Rv5q9~PuR;7$_-6A;2OdgO#Vo;t@L5ip#MVZV@U>5Q)8A+s8sBV2x zABaV}idu2ItxDHdS6xW)cWF#_i#pEq{@7!k6!U z_x|SI-~D~}kgQHgPVn;`RNMj^O$IHU zPr=M$BL!1dS_?teZ*(bjekW00r#LR5HadS~1D*QmXr@!M{ExfuPMC~tehZSR+D1fq zf5Q96<-_x;?)V2(wyxIfoGAz`dn0#p`_jFhf_MK`|GM0(>XkElT71IGTfZ*AF6Q`l zWTB^rK5Pgrs;{iP@b}XA^7t8JH%abbL31t@1mmg)L)UsPKYGe~=DR0?b4M)s^T}Oh zgDb^{Vm+gVdRj*09{j1Kw4|h2d@gy(Sli>n(1IJ9jFIx13G*Sy<2^biCPf_+(^U_s zV%s~V%EXh=V@@rY6<4|@d(tWy`N?0e)D5dYkamC$h5iVM*8wY4xt+JsI&~uLm4D zHv2$JpobT>YT=A&XTJ*g8%4Nb}=OOBV^pV($DTDNq(K~($3@PrTb>Gh-Q4{a@P zT)ab8KW*dGCCO3v`GKoLjtQzezc_Yhf2}ws{Zyv`& z*fC-$Ghw2Q3~kWEEGDioEMk;=J~)TF^3xfUlDgsbW(Ny^51|z|3NZmH)aishBg{-( z0RV9%b2JWdal`J@Kns@kqPRg zl^0d zuOJNsO)9vLs938NX)zIk$q5{h;24R>q+%I@VFX1;G?9^%R?-7Xtv55co}gGL04|^b z4i-Ub#3U+0BBc^JB9Y@5qCvG{L?V-GWMWc+;waSvVy1}(s>JhqX2n9007@h4ka||bGQSRaK)4qwURFq zpiWClKF(-C0JsCRo-|m@&Z9J1N6lhzmQO4a6{8p?63H=HM1)x6becn%%s`7QDuxQg zc1qaBC_p)YTAWoX0N^4(HHsJ$g);_In!%8-kK+@X9zvRtcA2`b1^hDYWp1+tg+3(aWYN35+-{#ZYWrAVu zS}1VBUV<4fphzxHfMq`-a&dhQ1?GoCuw8cAjTodFQWha6vxynRXc-G`qNd~k9syTCpd43lsH0UAT>UM%6x#TZhw$4D`ymUtRwI}d!#^43gGh=ra&P0-QgaG@Mn z1{o7G%n&p7=0LZQk+lAkbvQ#!8kg0B?R$sb?lTh>o@nV)UEvies#s0 z?VG+2_1pi^@W}5K*EWUYF`k!y{7`UxeZAc8+S3*FJ3@WvOTN6YMpdI~kZm6X`NL2# z^SX&YW37oIAIvPyBRD17?|D+dPkXE?zM!@odK4_Vw){rbj0pJ#Pjz zfxJOu+%7NemZOus%76dvng8{?Ru$KxB4h)j>;L&=bcLDt#+v)5TQV&76O2D~u%WwL{ z24Cp$!;TZ>(eID8>&h9yf;x9@LaLystIubrIW#gpZ&ZLBH$ zw$RqM)wjsj4Szo5QWFH(vK~Hb^X6pOyHcF>y*L>Dhh~b$e=ua^d?0N&V~stZ5OZM$AK+U&{Ap&rt5)v;3=u0nC_ zoYHW;)^6FY-MA*O{#-@KbGsl0{pR*Qsls-*dD4cFeLJSo>mGAMn;V#3-W8w6w=6jR zw$A~ikJIqL%`GcyIBBeERB@N%$=^5a(_@KVPm$9^_7d)dzeL??Ox literal 0 HcmV?d00001 diff --git a/Crawler/assets/nico-Ranger_512.png b/Crawler/assets/nico-Ranger_512.png new file mode 100644 index 0000000000000000000000000000000000000000..3e1dada735b3d2410278362102a87108bb4332d5 GIT binary patch literal 3388 zcmdT_do+}57k@Px9mn-(5Xw~4VRB0hawkkmu9I7?F(gckOrw~i+(#nGWf+~0+{(BX zay^)Cn2B*og*cj%ag7)e-?Tn!SxIHB@B8O_*Lv6cto^*t-uw64d;gvob2DRJZZU2E z0C*85hL!-ZjlJ9k>;SVrb6EN(002#U8yJ`)3=E*?0Dn*K^Bw@879Vf(#{ZS|PMsKk z#~fju?9%IekT|l9l|!|-*nJ8ZaWnV5zF2j8pKQ+4AU`pyUeyO(zBZ)!ZT>tg5Wb9S z&kd};7*ET5*#t)g!b%U)EtR8%>DTqTIUin#E+$(&Izy>SB!{Upg0-h!sOUaEoj#I^ z*yS?t;fx9?F@cntaum7u7pmUDIo-UwA!ap>^4sJo5ym~u&U>2%;9UL8)xaQRK^{md zOaJ_7tt)$Gyz$8eM~nHP14fI}GJ|@rXEcX*%O8lgb;YKP9BNjUkmOgh8vETXB`&A8 zQg&Fn<+HNV;(4A~iSWw(r{u=OYT9rX7bntPJ8f%DnXgh+{8jB|a5L{eygQIE{3xN@ zDfls6AgtJ@8quP#zy2L-*MTs|OdZ8hIv%T_`ynkoBz*y=f{&-qg2(lh-6N zFhUXIj~P&zB!jc51pJ8sDgVS~sklzT0Cd*_i8!j;o0oI5+DB-xk=<9@s7=EG zmC=cxv??PTY)st!Ja4?v)Hb>iCGs5V)a-HjG7Y^7x^$87P};3sqgTk=_TWEUxlz(; zuOtz-29tGeK}LTatqllvxc%xPf@RG@vMg9;EGO2kvu~5ob}U5IzRUX^37Op^4ENdb zt2l@5!zUj(St;fcDjaZ#XU7#x$R@2%NOU<~gnQ>6`uKq6Lu@Tp4oqyU>+YT}RU?Pn zgUq%=uzFE)YUREw20oppktVLM=zQgNeo8IGR7e*zE~HiPf{6sG>K3VrD`YQycNUDCaR>qm)9-0?YXGC4E{oU!7qCea z_rur{0CqDqHUt)cUZBbhrl8KY{DL;I3j}~)g}%HTKsr&BZRElrP9Ec8@Cf~;DrEUo z*_mzHg*j%6G4S{G_3*<01_2(f7>~2iU~h~U)EIHn{PZ7uA^@->A7Q9xjqRW4t2<|{ zCyIYd*wf64iVzm8=rj_ypjirJ?7Ukl&~)6;(y(MlirZT`uf6+_;kLG3+~TEWFS7fj z;*ns2KwQbW$Y=r_nv?d_b6%6W{c_vl{P^VaYf5- z2RkqT=(1OAtrGwMJbT3n{A6qGcTtN>2q4Yt^!k|%nA|QRxp?m4J(k_U+rr@w0r*y2 z?Eqju%i#QthT@e zGJJN~_~NABFwwauRHeKNep*tPq&_oLrcksB+bVr)h2IRmOLREII&o=v`gzx>qP9#d zx;loTqgvk!&kk?>_{YNFD#Y)H^#8#1`c`A41`J;&+{={$RIw|$PUv@fc>N?E*!=gt zLrv^T+;?G`%GjidyU#7NoSS$z^#AwHZbrje#6I1U@VjSWzr%9rOh$szJT6ZqOL7&q z83mhp-^{i)ppEDtB;e61TGVz@Mn$PSm*M}OSUDfNrMB5D;je7?cR@&6$eq9197nQc zq)EaKE19uMNOfjq!PQojHg2)qbm)K4{lpIL!Y8-hMH*!_JX3ckv@WDuiut&Dn%d)5 zMBQi7baXU}0(vrn{T_TcGqE&9+A20E@a=rBF{yYq6JjlhTUoBA(9)E-VyOfroI}Vx z%F#*&Jy#Z0ry4|?lH9C1Kk)gzt(AkedQ+pFAXJA$egJK03Q4h7V@}*$DAZhilK@Z0 z-eJQ;4iwm$tp@Vy^Y_jdB#fO8J+dvDDftopJroP~AylgWaO; z#c2AFZxCpUa{q(Lij-l)^MC=X99tT}uO)D;+rCm^FJ#F!*(X;*Szd~;a& zR6~7dn^QyR3Ufj1HM^`EFDKniY@0^<4-h=>c33u}48r2&+^W=l<88g%E*K0FXx|Y} xR9?z&+$a{`r256t#`M2&&W7{<@hBcz1rEyPG#U)NaQc!l;+UCXvA#?AzX9g3$JPJ< literal 0 HcmV?d00001 diff --git a/Crawler/assets/nico-Thief_512.png b/Crawler/assets/nico-Thief_512.png new file mode 100644 index 0000000000000000000000000000000000000000..1d336f8394da9481c66ee5bf91a8e84f40467574 GIT binary patch literal 2999 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4YzZe*pKV&*P2Y5O=D-;yvr)B1( zGB8;5_J-cgza1iC-;w#x0(~j-pppme`iua>$tct!Y)ky7j@k zV~_2axW3-408!)HC7;VGGL?^CSl{E7YZXH2sWQ{+7FsQ$%a`Lb%iT`_u0 zddFJbuN{t8Jb2HX{nqRM#id~@moux(bIJ*~t(E(j*)V0L^*Vlqdu~5JXx?|c^U>y! zte#p&L}JCX$2y0NW#suSf?sJQP3vBAeXG_Z)gyn64S(kFe3oh2s_d`(N@~}cs=%_h zvl1^w?D7xzf8I3TEb3#`$Csa;sr5hF-G4Q%{P-1t`gPemJ&))s?|b$`QmtO}FU|+`BDQY`%iz?6At!kT-z017Iwpm>8 z{;JDP6Q3(POie3Z6W>w&x_9G+{RZ!JeV#?tiM?K|`p&)J{^et*JE{fhU)*}day~L* zSz6rFf34bm@qEPs*K{|BJhAI^*}LGF!@KLtkL}R4eDGMYDB392xKG6C-=t)=%^f=@ ze$I1V+cHOb*6h>8_D}4#YArl}W!(ehpV>LjEnejPn(9jz*4KaeE|&d! z;+#zF`4#L34)bl9!Tj;pdcD6Jv_EZh(%VzL`SmLyADfl?SKeOv^$OR!)hoAlO#EfL zwQYZ>M{-`;go`$IZWG$YPb%y54>Mz)rwyRp)miL@` ze~SH7(Wl%`_Y{>!PrqH}`6J{<*pI*;em~-VNTxiTR1o>YbB9uma!k+MtB;>2e}2T#{T{L$w5j4>7q%$=Z6yYrJh%9Dc;1&j9Muu5) zB!GhKC7!;n?9X|Gh0Mi$_8(&h3Q3l@MwB?`=jNv7l`uFLr6!i7rYMwWmSiZnd-?{1 zH}Z)CwJCeLIEGZ*dV9k#@3w=0LtyTLB<(6 zt@!wbjt!A=acNcYkFkX$wur(erT`2sf)2unCmbd*Pg!9 z@u8p*n2{WS)}TZ#4il*777UZ1KDF7H#wubI&fn8vWM04jzIV*?w`JO!FQ54mca)QX z0jh{Z*CCWw7{QX`>GZT^@!axn5zAP0N8`njxgN@xNA&JGbF literal 0 HcmV?d00001 diff --git a/Crawler/assets/nico-Trapper_512.png b/Crawler/assets/nico-Trapper_512.png new file mode 100644 index 0000000000000000000000000000000000000000..fd5ea2738db95b2675c0159a7c88cc2307a4512f GIT binary patch literal 2999 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4YzZe*pKV&*P2Y5O=D-;yvr)B1( zGB8;5_J-cgza1iC-;w#x0(~j-pppme`iua>$tct!Y)ky7j@k zV~_2axW3-408!)HC7;VGGL?^CSl{E7YZXH2sWQ{+7FsQ$%a`Lb%iT`_u0 zddFJbuN{t8Jb2HX{nqRM#id~@moux(bIJ*~t(E(j*)V0L^*Vlqdu~5JXx?|c^U>y! zte#p&L}JCX$2y0NW#suSf?sJQP3vBAeXG_Z)gyn64S(kFe3oh2s_d`(N@~}cs=%_h zvl1^w?D7xzf8I3TEb3#`$Csa;sr5hF-G4Q%{P-1t`gPemJ&))s?|b$`QmtO}FU|+`BDQY`%iz?6At!kT-z017Iwpm>8 z{;JDP6Q3(POie3Z6W>w&x_9G+{RZ!JeV#?tiM?K|`p&)J{^et*JE{fhU)*}day~L* zSz6rFf34bm@qEPs*K{|BJhAI^*}LGF!@KLtkL}R4eDGMYDB392xKG6C-=t)=%^f=@ ze$I1V+cHOb*6h>8_D}4#YArl}W!(ehpV>LjEnejPn(9jz*4KaeE|&d! z;+#zF`4#L34)bl9!Tj;pdcD6Jv_EZh(%VzL`SmLyADfl?SKeOv^$OR!)hoAlO#EfL zwQYZ>M{-`;go`$IZWG$YPb%y54>Mz)rwyRp)miL@` ze~SH7(Wl%`_Y{>!PrqH}`6J{<*pI*;em~-VNTxiTR1o>YbB9uma!k+MtB;>2e}2T#{T{L$w5j4>7q%$=Z6yYrJh%9Dc;1&j9Muu5) zB!GhKC7!;n?9X|Gh0G0JZY|CM3Q3l@MwB?`=jNv7l`uFLr6!i7rYMwWmSiZnd-?{1 zH}Z)CwJCeLIEGZ*dV9k#@3w=0LtyTLB<(6 zt@!wbjt!A=acNcYkFkX$wur(erT`2sf)2unCmbd*Pg!9 z@u8p*n2{WS)}TZ#4il*777UZ1KDF7H#wubI&fn8vWM04jzIV*?w`JO!FQ54mca)QX z0jh{Z*CCWw7{QX`>GZT^@!axn5zAP0N8`njxgN@xNAN;(nW literal 0 HcmV?d00001 diff --git a/Crawler/assets/nico-Warrior_512.png b/Crawler/assets/nico-Warrior_512.png new file mode 100644 index 0000000000000000000000000000000000000000..1c8ea73e3286935fede210e7b1afc1d082a987c0 GIT binary patch literal 2999 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4YzZe*pKV&*P2Y5O=D-;yvr)B1( zGB8;5_J-cgza1iC-;w#x0(~j-pppme`iua>$tct!Y)ky7j@k zV~_2axW3-408!)HC7;VGGL?^CSl{E7YZXH2sWQ{+7FsQ$%a`Lb%iT`_u0 zddFJbuN{t8Jb2HX{nqRM#id~@moux(bIJ*~t(E(j*)V0L^*Vlqdu~5JXx?|c^U>y! zte#p&L}JCX$2y0NW#suSf?sJQP3vBAeXG_Z)gyn64S(kFe3oh2s_d`(N@~}cs=%_h zvl1^w?D7xzf8I3TEb3#`$Csa;sr5hF-G4Q%{P-1t`gPemJ&))s?|b$`QmtO}FU|+`BDQY`%iz?6At!kT-z017Iwpm>8 z{;JDP6Q3(POie3Z6W>w&x_9G+{RZ!JeV#?tiM?K|`p&)J{^et*JE{fhU)*}day~L* zSz6rFf34bm@qEPs*K{|BJhAI^*}LGF!@KLtkL}R4eDGMYDB392xKG6C-=t)=%^f=@ ze$I1V+cHOb*6h>8_D}4#YArl}W!(ehpV>LjEnejPn(9jz*4KaeE|&d! z;+#zF`4#L34)bl9!Tj;pdcD6Jv_EZh(%VzL`SmLyADfl?SKeOv^$OR!)hoAlO#EfL zwQYZ>M{-`;go`$IZWG$YPb%y54>Mz)rwyRp)miL@` ze~SH7(Wl%`_Y{>!PrqH}`6J{<*pI*;em~-VNTxiTR1o>YbB9uma!k+MtB;>2e}2T#{T{L$w5j4>7q%$=Z6yYrJh%9Dc;1&j9Muu5) zB!GhKC7!;n?9X|Gg-p#|pSSn|g(OQ{BTAg}b8}PkN*J7rQWHy3QxwWGOEMJPJ$(bh z8~Mb6+LS$A978H@y}ekR@rw^a@ zd7nLpSm)vPO#`#3w#D9^RS7OPv~;G;+{b7EREzFWqCAf^$Z%VXRXM_9Na!30&JH`k zR($+I$A(C`xU{PH$Jjy=TSQ?KQve1RK?h;P6AqIa*6)sDTr+iM^s_!guRHbcYfsk!H-j9|&}bb8vdc<%cQM5jkYJVT6Y00tRCEacjn2Vb7OYE16Y6~9#-Z)dDD z?fjpQA2&VyxBq{g{zr$uwZFHfzI#1?{&cSMulk;Exv}-vI&8@nH5Q=WMTr^IAZX7D z3pf>HThJ5u>{<1D)pf>7HY=r5o;^KtzJB5VDet?m#Vx7{5W}&FH86mESg>NX=#)!g z{BQR9XR{Unl_P?a*n|g)Y!pufT)WEUw?21<<=UxSRqs!y_ZgmwdA;nvy&up@g2~;1 zfdT3?1uV)BESx1;P;{AV4|BIo@V)EU0vV5PtWxs}wlZlf7TtO7WjT{NUdy4{35O8S f;X?u(2>i!5wcUf=^~97Ukb^y4{an^LB{Ts5y#5hn literal 0 HcmV?d00001 diff --git a/Crawler/assets/nico-Witch_512.png b/Crawler/assets/nico-Witch_512.png new file mode 100644 index 0000000000000000000000000000000000000000..c3036ebeb7b781a4b99b403e2c3fe78a86fddb52 GIT binary patch literal 2999 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4YzZe*pKV&*P2Y5O=D-;yvr)B1( zGB8;5_J-cgza1iC-;w#x0(~j-pppme`iua>$tct!Y)ky7j@k zV~_2axW3-408!)HC7;VGGL?^CSl{E7YZXH2sWQ{+7FsQ$%a`Lb%iT`_u0 zddFJbuN{t8Jb2HX{nqRM#id~@moux(bIJ*~t(E(j*)V0L^*Vlqdu~5JXx?|c^U>y! zte#p&L}JCX$2y0NW#suSf?sJQP3vBAeXG_Z)gyn64S(kFe3oh2s_d`(N@~}cs=%_h zvl1^w?D7xzf8I3TEb3#`$Csa;sr5hF-G4Q%{P-1t`gPemJ&))s?|b$`QmtO}FU|+`BDQY`%iz?6At!kT-z017Iwpm>8 z{;JDP6Q3(POie3Z6W>w&x_9G+{RZ!JeV#?tiM?K|`p&)J{^et*JE{fhU)*}day~L* zSz6rFf34bm@qEPs*K{|BJhAI^*}LGF!@KLtkL}R4eDGMYDB392xKG6C-=t)=%^f=@ ze$I1V+cHOb*6h>8_D}4#YArl}W!(ehpV>LjEnejPn(9jz*4KaeE|&d! z;+#zF`4#L34)bl9!Tj;pdcD6Jv_EZh(%VzL`SmLyADfl?SKeOv^$OR!)hoAlO#EfL zwQYZ>M{-`;go`$IZWG$YPb%y54>Mz)rwyRp)miL@` ze~SH7(Wl%`_Y{>!PrqH}`6J{<*pI*;em~-VNTxiTR1o>YbB9uma!k+MtB;>2e}2T#{T{L$w5j4>7q%$=Z6yYrJh%9Dc;1&j9Muu5) zB!GhKC7!;n?9X|Gh0M(uu3d`+3Q3l@MwB?`=jNv7l`uFLr6!i7rYMwWmSiZnd-?{1 zH}Z)CwJCeLIEGZ*dV9k#@3w=0LtyTLB<(6 zt@!wbjt!A=acNcYkFkX$wur(erT`2sf)2unCmbd*Pg!9 z@u8p*n2{WS)}TZ#4il*777UZ1KDF7H#wubI&fn8vWM04jzIV*?w`JO!FQ54mca)QX z0jh{Z*CCWw7{QX`>GZT^@!axn5zAP0N8`njxgN@xNA{>~9? literal 0 HcmV?d00001 diff --git a/Crawler/assets/nico-Wizard_512.png b/Crawler/assets/nico-Wizard_512.png new file mode 100644 index 0000000000000000000000000000000000000000..c192b0f091589260041a40d14b497a5a2aaa0588 GIT binary patch literal 6869 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 literal 0 HcmV?d00001 diff --git a/Crawler/discord-files/achievement_manager.cpp b/Crawler/discord-files/achievement_manager.cpp new file mode 100644 index 00000000..43a6d4c9 --- /dev/null +++ b/Crawler/discord-files/achievement_manager.cpp @@ -0,0 +1,99 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "achievement_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class AchievementEvents final { +public: + static void DISCORD_CALLBACK OnUserAchievementUpdate(void* callbackData, + DiscordUserAchievement* userAchievement) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->AchievementManager(); + module.OnUserAchievementUpdate(*reinterpret_cast(userAchievement)); + } +}; + +IDiscordAchievementEvents AchievementManager::events_{ + &AchievementEvents::OnUserAchievementUpdate, +}; + +void AchievementManager::SetUserAchievement(Snowflake achievementId, + std::uint8_t percentComplete, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->set_user_achievement( + internal_, achievementId, percentComplete, cb.release(), wrapper); +} + +void AchievementManager::FetchUserAchievements(std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->fetch_user_achievements(internal_, cb.release(), wrapper); +} + +void AchievementManager::CountUserAchievements(std::int32_t* count) +{ + if (!count) { + return; + } + + internal_->count_user_achievements(internal_, reinterpret_cast(count)); +} + +Result AchievementManager::GetUserAchievement(Snowflake userAchievementId, + UserAchievement* userAchievement) +{ + if (!userAchievement) { + return Result::InternalError; + } + + auto result = internal_->get_user_achievement( + internal_, userAchievementId, reinterpret_cast(userAchievement)); + return static_cast(result); +} + +Result AchievementManager::GetUserAchievementAt(std::int32_t index, + UserAchievement* userAchievement) +{ + if (!userAchievement) { + return Result::InternalError; + } + + auto result = internal_->get_user_achievement_at( + internal_, index, reinterpret_cast(userAchievement)); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/achievement_manager.h b/Crawler/discord-files/achievement_manager.h new file mode 100644 index 00000000..1f58c8eb --- /dev/null +++ b/Crawler/discord-files/achievement_manager.h @@ -0,0 +1,34 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class AchievementManager final { +public: + ~AchievementManager() = default; + + void SetUserAchievement(Snowflake achievementId, + std::uint8_t percentComplete, + std::function callback); + void FetchUserAchievements(std::function callback); + void CountUserAchievements(std::int32_t* count); + Result GetUserAchievement(Snowflake userAchievementId, UserAchievement* userAchievement); + Result GetUserAchievementAt(std::int32_t index, UserAchievement* userAchievement); + + Event OnUserAchievementUpdate; + +private: + friend class Core; + + AchievementManager() = default; + AchievementManager(AchievementManager const& rhs) = delete; + AchievementManager& operator=(AchievementManager const& rhs) = delete; + AchievementManager(AchievementManager&& rhs) = delete; + AchievementManager& operator=(AchievementManager&& rhs) = delete; + + IDiscordAchievementManager* internal_; + static IDiscordAchievementEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/activity_manager.cpp b/Crawler/discord-files/activity_manager.cpp new file mode 100644 index 00000000..3c200746 --- /dev/null +++ b/Crawler/discord-files/activity_manager.cpp @@ -0,0 +1,177 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "activity_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class ActivityEvents final { +public: + static void DISCORD_CALLBACK OnActivityJoin(void* callbackData, char const* secret) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->ActivityManager(); + module.OnActivityJoin(static_cast(secret)); + } + + static void DISCORD_CALLBACK OnActivitySpectate(void* callbackData, char const* secret) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->ActivityManager(); + module.OnActivitySpectate(static_cast(secret)); + } + + static void DISCORD_CALLBACK OnActivityJoinRequest(void* callbackData, DiscordUser* user) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->ActivityManager(); + module.OnActivityJoinRequest(*reinterpret_cast(user)); + } + + static void DISCORD_CALLBACK OnActivityInvite(void* callbackData, + EDiscordActivityActionType type, + DiscordUser* user, + DiscordActivity* activity) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->ActivityManager(); + module.OnActivityInvite(static_cast(type), + *reinterpret_cast(user), + *reinterpret_cast(activity)); + } +}; + +IDiscordActivityEvents ActivityManager::events_{ + &ActivityEvents::OnActivityJoin, + &ActivityEvents::OnActivitySpectate, + &ActivityEvents::OnActivityJoinRequest, + &ActivityEvents::OnActivityInvite, +}; + +Result ActivityManager::RegisterCommand(char const* command) +{ + auto result = internal_->register_command(internal_, const_cast(command)); + return static_cast(result); +} + +Result ActivityManager::RegisterSteam(std::uint32_t steamId) +{ + auto result = internal_->register_steam(internal_, steamId); + return static_cast(result); +} + +void ActivityManager::UpdateActivity(Activity const& activity, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->update_activity(internal_, + reinterpret_cast(const_cast(&activity)), + cb.release(), + wrapper); +} + +void ActivityManager::ClearActivity(std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->clear_activity(internal_, cb.release(), wrapper); +} + +void ActivityManager::SendRequestReply(UserId userId, + ActivityJoinRequestReply reply, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->send_request_reply(internal_, + userId, + static_cast(reply), + cb.release(), + wrapper); +} + +void ActivityManager::SendInvite(UserId userId, + ActivityActionType type, + char const* content, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->send_invite(internal_, + userId, + static_cast(type), + const_cast(content), + cb.release(), + wrapper); +} + +void ActivityManager::AcceptInvite(UserId userId, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->accept_invite(internal_, userId, cb.release(), wrapper); +} + +} // namespace discord diff --git a/Crawler/discord-files/activity_manager.h b/Crawler/discord-files/activity_manager.h new file mode 100644 index 00000000..c107be27 --- /dev/null +++ b/Crawler/discord-files/activity_manager.h @@ -0,0 +1,42 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class ActivityManager final { +public: + ~ActivityManager() = default; + + Result RegisterCommand(char const* command); + Result RegisterSteam(std::uint32_t steamId); + void UpdateActivity(Activity const& activity, std::function callback); + void ClearActivity(std::function callback); + void SendRequestReply(UserId userId, + ActivityJoinRequestReply reply, + std::function callback); + void SendInvite(UserId userId, + ActivityActionType type, + char const* content, + std::function callback); + void AcceptInvite(UserId userId, std::function callback); + + Event OnActivityJoin; + Event OnActivitySpectate; + Event OnActivityJoinRequest; + Event OnActivityInvite; + +private: + friend class Core; + + ActivityManager() = default; + ActivityManager(ActivityManager const& rhs) = delete; + ActivityManager& operator=(ActivityManager const& rhs) = delete; + ActivityManager(ActivityManager&& rhs) = delete; + ActivityManager& operator=(ActivityManager&& rhs) = delete; + + IDiscordActivityManager* internal_; + static IDiscordActivityEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/application_manager.cpp b/Crawler/discord-files/application_manager.cpp new file mode 100644 index 00000000..0e05f3f3 --- /dev/null +++ b/Crawler/discord-files/application_manager.cpp @@ -0,0 +1,78 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "application_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +void ApplicationManager::ValidateOrExit(std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->validate_or_exit(internal_, cb.release(), wrapper); +} + +void ApplicationManager::GetCurrentLocale(char locale[128]) +{ + if (!locale) { + return; + } + + internal_->get_current_locale(internal_, reinterpret_cast(locale)); +} + +void ApplicationManager::GetCurrentBranch(char branch[4096]) +{ + if (!branch) { + return; + } + + internal_->get_current_branch(internal_, reinterpret_cast(branch)); +} + +void ApplicationManager::GetOAuth2Token(std::function callback) +{ + static auto wrapper = + [](void* callbackData, EDiscordResult result, DiscordOAuth2Token* oauth2Token) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), *reinterpret_cast(oauth2Token)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->get_oauth2_token(internal_, cb.release(), wrapper); +} + +void ApplicationManager::GetTicket(std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result, char const* data) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), static_cast(data)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->get_ticket(internal_, cb.release(), wrapper); +} + +} // namespace discord diff --git a/Crawler/discord-files/application_manager.h b/Crawler/discord-files/application_manager.h new file mode 100644 index 00000000..ab0e8563 --- /dev/null +++ b/Crawler/discord-files/application_manager.h @@ -0,0 +1,30 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class ApplicationManager final { +public: + ~ApplicationManager() = default; + + void ValidateOrExit(std::function callback); + void GetCurrentLocale(char locale[128]); + void GetCurrentBranch(char branch[4096]); + void GetOAuth2Token(std::function callback); + void GetTicket(std::function callback); + +private: + friend class Core; + + ApplicationManager() = default; + ApplicationManager(ApplicationManager const& rhs) = delete; + ApplicationManager& operator=(ApplicationManager const& rhs) = delete; + ApplicationManager(ApplicationManager&& rhs) = delete; + ApplicationManager& operator=(ApplicationManager&& rhs) = delete; + + IDiscordApplicationManager* internal_; + static IDiscordApplicationEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/core.cpp b/Crawler/discord-files/core.cpp new file mode 100644 index 00000000..110c9ef6 --- /dev/null +++ b/Crawler/discord-files/core.cpp @@ -0,0 +1,182 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "core.h" + +#include +#include + +namespace discord { + +Result Core::Create(ClientId clientId, std::uint64_t flags, Core** instance) +{ + if (!instance) { + return Result::InternalError; + } + + (*instance) = new Core(); + DiscordCreateParams params{}; + DiscordCreateParamsSetDefault(¶ms); + params.client_id = clientId; + params.flags = flags; + params.events = nullptr; + params.event_data = *instance; + params.user_events = &UserManager::events_; + params.activity_events = &ActivityManager::events_; + params.relationship_events = &RelationshipManager::events_; + params.lobby_events = &LobbyManager::events_; + params.network_events = &NetworkManager::events_; + params.overlay_events = &OverlayManager::events_; + params.store_events = &StoreManager::events_; + params.voice_events = &VoiceManager::events_; + params.achievement_events = &AchievementManager::events_; + auto result = DiscordCreate(DISCORD_VERSION, ¶ms, &((*instance)->internal_)); + if (result != DiscordResult_Ok || !(*instance)->internal_) { + delete (*instance); + (*instance) = nullptr; + } + + return static_cast(result); +} + +Core::~Core() +{ + if (internal_) { + internal_->destroy(internal_); + internal_ = nullptr; + } +} + +Result Core::RunCallbacks() +{ + auto result = internal_->run_callbacks(internal_); + return static_cast(result); +} + +void Core::SetLogHook(LogLevel minLevel, std::function hook) +{ + setLogHook_.DisconnectAll(); + setLogHook_.Connect(std::move(hook)); + static auto wrapper = + [](void* callbackData, EDiscordLogLevel level, char const* message) -> void { + auto cb(reinterpret_cast(callbackData)); + if (!cb) { + return; + } + (*cb)(static_cast(level), static_cast(message)); + }; + + internal_->set_log_hook( + internal_, static_cast(minLevel), &setLogHook_, wrapper); +} + +discord::ApplicationManager& Core::ApplicationManager() +{ + if (!applicationManager_.internal_) { + applicationManager_.internal_ = internal_->get_application_manager(internal_); + } + + return applicationManager_; +} + +discord::UserManager& Core::UserManager() +{ + if (!userManager_.internal_) { + userManager_.internal_ = internal_->get_user_manager(internal_); + } + + return userManager_; +} + +discord::ImageManager& Core::ImageManager() +{ + if (!imageManager_.internal_) { + imageManager_.internal_ = internal_->get_image_manager(internal_); + } + + return imageManager_; +} + +discord::ActivityManager& Core::ActivityManager() +{ + if (!activityManager_.internal_) { + activityManager_.internal_ = internal_->get_activity_manager(internal_); + } + + return activityManager_; +} + +discord::RelationshipManager& Core::RelationshipManager() +{ + if (!relationshipManager_.internal_) { + relationshipManager_.internal_ = internal_->get_relationship_manager(internal_); + } + + return relationshipManager_; +} + +discord::LobbyManager& Core::LobbyManager() +{ + if (!lobbyManager_.internal_) { + lobbyManager_.internal_ = internal_->get_lobby_manager(internal_); + } + + return lobbyManager_; +} + +discord::NetworkManager& Core::NetworkManager() +{ + if (!networkManager_.internal_) { + networkManager_.internal_ = internal_->get_network_manager(internal_); + } + + return networkManager_; +} + +discord::OverlayManager& Core::OverlayManager() +{ + if (!overlayManager_.internal_) { + overlayManager_.internal_ = internal_->get_overlay_manager(internal_); + } + + return overlayManager_; +} + +discord::StorageManager& Core::StorageManager() +{ + if (!storageManager_.internal_) { + storageManager_.internal_ = internal_->get_storage_manager(internal_); + } + + return storageManager_; +} + +discord::StoreManager& Core::StoreManager() +{ + if (!storeManager_.internal_) { + storeManager_.internal_ = internal_->get_store_manager(internal_); + } + + return storeManager_; +} + +discord::VoiceManager& Core::VoiceManager() +{ + if (!voiceManager_.internal_) { + voiceManager_.internal_ = internal_->get_voice_manager(internal_); + } + + return voiceManager_; +} + +discord::AchievementManager& Core::AchievementManager() +{ + if (!achievementManager_.internal_) { + achievementManager_.internal_ = internal_->get_achievement_manager(internal_); + } + + return achievementManager_; +} + +} // namespace discord diff --git a/Crawler/discord-files/core.h b/Crawler/discord-files/core.h new file mode 100644 index 00000000..8af6fca2 --- /dev/null +++ b/Crawler/discord-files/core.h @@ -0,0 +1,64 @@ +#pragma once + +#include "types.h" +#include "application_manager.h" +#include "user_manager.h" +#include "image_manager.h" +#include "activity_manager.h" +#include "relationship_manager.h" +#include "lobby_manager.h" +#include "network_manager.h" +#include "overlay_manager.h" +#include "storage_manager.h" +#include "store_manager.h" +#include "voice_manager.h" +#include "achievement_manager.h" + +namespace discord { + +class Core final { +public: + static Result Create(ClientId clientId, std::uint64_t flags, Core** instance); + + ~Core(); + + Result RunCallbacks(); + void SetLogHook(LogLevel minLevel, std::function hook); + + discord::ApplicationManager& ApplicationManager(); + discord::UserManager& UserManager(); + discord::ImageManager& ImageManager(); + discord::ActivityManager& ActivityManager(); + discord::RelationshipManager& RelationshipManager(); + discord::LobbyManager& LobbyManager(); + discord::NetworkManager& NetworkManager(); + discord::OverlayManager& OverlayManager(); + discord::StorageManager& StorageManager(); + discord::StoreManager& StoreManager(); + discord::VoiceManager& VoiceManager(); + discord::AchievementManager& AchievementManager(); + +private: + Core() = default; + Core(Core const& rhs) = delete; + Core& operator=(Core const& rhs) = delete; + Core(Core&& rhs) = delete; + Core& operator=(Core&& rhs) = delete; + + IDiscordCore* internal_; + Event setLogHook_; + discord::ApplicationManager applicationManager_; + discord::UserManager userManager_; + discord::ImageManager imageManager_; + discord::ActivityManager activityManager_; + discord::RelationshipManager relationshipManager_; + discord::LobbyManager lobbyManager_; + discord::NetworkManager networkManager_; + discord::OverlayManager overlayManager_; + discord::StorageManager storageManager_; + discord::StoreManager storeManager_; + discord::VoiceManager voiceManager_; + discord::AchievementManager achievementManager_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/discord.h b/Crawler/discord-files/discord.h new file mode 100644 index 00000000..c9912129 --- /dev/null +++ b/Crawler/discord-files/discord.h @@ -0,0 +1,16 @@ +#pragma once + +#include "types.h" +#include "core.h" +#include "application_manager.h" +#include "user_manager.h" +#include "image_manager.h" +#include "activity_manager.h" +#include "relationship_manager.h" +#include "lobby_manager.h" +#include "network_manager.h" +#include "overlay_manager.h" +#include "storage_manager.h" +#include "store_manager.h" +#include "voice_manager.h" +#include "achievement_manager.h" diff --git a/Crawler/discord-files/event.h b/Crawler/discord-files/event.h new file mode 100644 index 00000000..610887d1 --- /dev/null +++ b/Crawler/discord-files/event.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +namespace discord { + +template +class Event final { +public: + using Token = int; + + Event() { slots_.reserve(4); } + + Event(Event const&) = default; + Event(Event&&) = default; + ~Event() = default; + + Event& operator=(Event const&) = default; + Event& operator=(Event&&) = default; + + template + Token Connect(EventHandler slot) + { + slots_.emplace_back(Slot{nextToken_, std::move(slot)}); + return nextToken_++; + } + + void Disconnect(Token token) + { + for (auto& slot : slots_) { + if (slot.token == token) { + slot = slots_.back(); + slots_.pop_back(); + break; + } + } + } + + void DisconnectAll() { slots_ = {}; } + + void operator()(Args... args) + { + for (auto const& slot : slots_) { + slot.fn(std::forward(args)...); + } + } + +private: + struct Slot { + Token token; + std::function fn; + }; + + Token nextToken_{}; + std::vector slots_{}; +}; + +} // namespace discord diff --git a/Crawler/discord-files/ffi.h b/Crawler/discord-files/ffi.h new file mode 100644 index 00000000..4a210574 --- /dev/null +++ b/Crawler/discord-files/ffi.h @@ -0,0 +1,1113 @@ +#ifndef _DISCORD_GAME_SDK_H_ +#define _DISCORD_GAME_SDK_H_ + +#ifdef _WIN32 +#include +#include +#endif + +#ifdef _WIN32 +#ifdef _WIN64 +#define DISCORD_API +#else +#define DISCORD_API __stdcall +#endif +#else +#define DISCORD_API +#endif + +#define DISCORD_CALLBACK DISCORD_API + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#ifndef __cplusplus +#include +#endif + +#define DISCORD_VERSION 3 +#define DISCORD_APPLICATION_MANAGER_VERSION 1 +#define DISCORD_USER_MANAGER_VERSION 1 +#define DISCORD_IMAGE_MANAGER_VERSION 1 +#define DISCORD_ACTIVITY_MANAGER_VERSION 1 +#define DISCORD_RELATIONSHIP_MANAGER_VERSION 1 +#define DISCORD_LOBBY_MANAGER_VERSION 1 +#define DISCORD_NETWORK_MANAGER_VERSION 1 +#define DISCORD_OVERLAY_MANAGER_VERSION 2 +#define DISCORD_STORAGE_MANAGER_VERSION 1 +#define DISCORD_STORE_MANAGER_VERSION 1 +#define DISCORD_VOICE_MANAGER_VERSION 1 +#define DISCORD_ACHIEVEMENT_MANAGER_VERSION 1 + +enum EDiscordResult { + DiscordResult_Ok = 0, + DiscordResult_ServiceUnavailable = 1, + DiscordResult_InvalidVersion = 2, + DiscordResult_LockFailed = 3, + DiscordResult_InternalError = 4, + DiscordResult_InvalidPayload = 5, + DiscordResult_InvalidCommand = 6, + DiscordResult_InvalidPermissions = 7, + DiscordResult_NotFetched = 8, + DiscordResult_NotFound = 9, + DiscordResult_Conflict = 10, + DiscordResult_InvalidSecret = 11, + DiscordResult_InvalidJoinSecret = 12, + DiscordResult_NoEligibleActivity = 13, + DiscordResult_InvalidInvite = 14, + DiscordResult_NotAuthenticated = 15, + DiscordResult_InvalidAccessToken = 16, + DiscordResult_ApplicationMismatch = 17, + DiscordResult_InvalidDataUrl = 18, + DiscordResult_InvalidBase64 = 19, + DiscordResult_NotFiltered = 20, + DiscordResult_LobbyFull = 21, + DiscordResult_InvalidLobbySecret = 22, + DiscordResult_InvalidFilename = 23, + DiscordResult_InvalidFileSize = 24, + DiscordResult_InvalidEntitlement = 25, + DiscordResult_NotInstalled = 26, + DiscordResult_NotRunning = 27, + DiscordResult_InsufficientBuffer = 28, + DiscordResult_PurchaseCanceled = 29, + DiscordResult_InvalidGuild = 30, + DiscordResult_InvalidEvent = 31, + DiscordResult_InvalidChannel = 32, + DiscordResult_InvalidOrigin = 33, + DiscordResult_RateLimited = 34, + DiscordResult_OAuth2Error = 35, + DiscordResult_SelectChannelTimeout = 36, + DiscordResult_GetGuildTimeout = 37, + DiscordResult_SelectVoiceForceRequired = 38, + DiscordResult_CaptureShortcutAlreadyListening = 39, + DiscordResult_UnauthorizedForAchievement = 40, + DiscordResult_InvalidGiftCode = 41, + DiscordResult_PurchaseError = 42, + DiscordResult_TransactionAborted = 43, + DiscordResult_DrawingInitFailed = 44, +}; + +enum EDiscordCreateFlags { + DiscordCreateFlags_Default = 0, + DiscordCreateFlags_NoRequireDiscord = 1, +}; + +enum EDiscordLogLevel { + DiscordLogLevel_Error = 1, + DiscordLogLevel_Warn, + DiscordLogLevel_Info, + DiscordLogLevel_Debug, +}; + +enum EDiscordUserFlag { + DiscordUserFlag_Partner = 2, + DiscordUserFlag_HypeSquadEvents = 4, + DiscordUserFlag_HypeSquadHouse1 = 64, + DiscordUserFlag_HypeSquadHouse2 = 128, + DiscordUserFlag_HypeSquadHouse3 = 256, +}; + +enum EDiscordPremiumType { + DiscordPremiumType_None = 0, + DiscordPremiumType_Tier1 = 1, + DiscordPremiumType_Tier2 = 2, +}; + +enum EDiscordImageType { + DiscordImageType_User, +}; + +enum EDiscordActivityPartyPrivacy { + DiscordActivityPartyPrivacy_Private = 0, + DiscordActivityPartyPrivacy_Public = 1, +}; + +enum EDiscordActivityType { + DiscordActivityType_Playing, + DiscordActivityType_Streaming, + DiscordActivityType_Listening, + DiscordActivityType_Watching, +}; + +enum EDiscordActivityActionType { + DiscordActivityActionType_Join = 1, + DiscordActivityActionType_Spectate, +}; + +enum EDiscordActivitySupportedPlatformFlags { + DiscordActivitySupportedPlatformFlags_Desktop = 1, + DiscordActivitySupportedPlatformFlags_Android = 2, + DiscordActivitySupportedPlatformFlags_iOS = 4, +}; + +enum EDiscordActivityJoinRequestReply { + DiscordActivityJoinRequestReply_No, + DiscordActivityJoinRequestReply_Yes, + DiscordActivityJoinRequestReply_Ignore, +}; + +enum EDiscordStatus { + DiscordStatus_Offline = 0, + DiscordStatus_Online = 1, + DiscordStatus_Idle = 2, + DiscordStatus_DoNotDisturb = 3, +}; + +enum EDiscordRelationshipType { + DiscordRelationshipType_None, + DiscordRelationshipType_Friend, + DiscordRelationshipType_Blocked, + DiscordRelationshipType_PendingIncoming, + DiscordRelationshipType_PendingOutgoing, + DiscordRelationshipType_Implicit, +}; + +enum EDiscordLobbyType { + DiscordLobbyType_Private = 1, + DiscordLobbyType_Public, +}; + +enum EDiscordLobbySearchComparison { + DiscordLobbySearchComparison_LessThanOrEqual = -2, + DiscordLobbySearchComparison_LessThan, + DiscordLobbySearchComparison_Equal, + DiscordLobbySearchComparison_GreaterThan, + DiscordLobbySearchComparison_GreaterThanOrEqual, + DiscordLobbySearchComparison_NotEqual, +}; + +enum EDiscordLobbySearchCast { + DiscordLobbySearchCast_String = 1, + DiscordLobbySearchCast_Number, +}; + +enum EDiscordLobbySearchDistance { + DiscordLobbySearchDistance_Local, + DiscordLobbySearchDistance_Default, + DiscordLobbySearchDistance_Extended, + DiscordLobbySearchDistance_Global, +}; + +enum EDiscordKeyVariant { + DiscordKeyVariant_Normal, + DiscordKeyVariant_Right, + DiscordKeyVariant_Left, +}; + +enum EDiscordMouseButton { + DiscordMouseButton_Left, + DiscordMouseButton_Middle, + DiscordMouseButton_Right, +}; + +enum EDiscordEntitlementType { + DiscordEntitlementType_Purchase = 1, + DiscordEntitlementType_PremiumSubscription, + DiscordEntitlementType_DeveloperGift, + DiscordEntitlementType_TestModePurchase, + DiscordEntitlementType_FreePurchase, + DiscordEntitlementType_UserGift, + DiscordEntitlementType_PremiumPurchase, +}; + +enum EDiscordSkuType { + DiscordSkuType_Application = 1, + DiscordSkuType_DLC, + DiscordSkuType_Consumable, + DiscordSkuType_Bundle, +}; + +enum EDiscordInputModeType { + DiscordInputModeType_VoiceActivity = 0, + DiscordInputModeType_PushToTalk, +}; + +typedef int64_t DiscordClientId; +typedef int32_t DiscordVersion; +typedef int64_t DiscordSnowflake; +typedef int64_t DiscordTimestamp; +typedef DiscordSnowflake DiscordUserId; +typedef char DiscordLocale[128]; +typedef char DiscordBranch[4096]; +typedef DiscordSnowflake DiscordLobbyId; +typedef char DiscordLobbySecret[128]; +typedef char DiscordMetadataKey[256]; +typedef char DiscordMetadataValue[4096]; +typedef uint64_t DiscordNetworkPeerId; +typedef uint8_t DiscordNetworkChannelId; +#ifdef __APPLE__ +typedef void IDXGISwapChain; +#endif +#ifdef __linux__ +typedef void IDXGISwapChain; +#endif +#ifdef __APPLE__ +typedef void MSG; +#endif +#ifdef __linux__ +typedef void MSG; +#endif +typedef char DiscordPath[4096]; +typedef char DiscordDateTime[64]; + +struct DiscordUser { + DiscordUserId id; + char username[256]; + char discriminator[8]; + char avatar[128]; + bool bot; +}; + +struct DiscordOAuth2Token { + char access_token[128]; + char scopes[1024]; + DiscordTimestamp expires; +}; + +struct DiscordImageHandle { + enum EDiscordImageType type; + int64_t id; + uint32_t size; +}; + +struct DiscordImageDimensions { + uint32_t width; + uint32_t height; +}; + +struct DiscordActivityTimestamps { + DiscordTimestamp start; + DiscordTimestamp end; +}; + +struct DiscordActivityAssets { + char large_image[128]; + char large_text[128]; + char small_image[128]; + char small_text[128]; +}; + +struct DiscordPartySize { + int32_t current_size; + int32_t max_size; +}; + +struct DiscordActivityParty { + char id[128]; + struct DiscordPartySize size; + enum EDiscordActivityPartyPrivacy privacy; +}; + +struct DiscordActivitySecrets { + char match[128]; + char join[128]; + char spectate[128]; +}; + +struct DiscordActivity { + enum EDiscordActivityType type; + int64_t application_id; + char name[128]; + char state[128]; + char details[128]; + struct DiscordActivityTimestamps timestamps; + struct DiscordActivityAssets assets; + struct DiscordActivityParty party; + struct DiscordActivitySecrets secrets; + bool instance; + uint32_t supported_platforms; +}; + +struct DiscordPresence { + enum EDiscordStatus status; + struct DiscordActivity activity; +}; + +struct DiscordRelationship { + enum EDiscordRelationshipType type; + struct DiscordUser user; + struct DiscordPresence presence; +}; + +struct DiscordLobby { + DiscordLobbyId id; + enum EDiscordLobbyType type; + DiscordUserId owner_id; + DiscordLobbySecret secret; + uint32_t capacity; + bool locked; +}; + +struct DiscordImeUnderline { + int32_t from; + int32_t to; + uint32_t color; + uint32_t background_color; + bool thick; +}; + +struct DiscordRect { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +}; + +struct DiscordFileStat { + char filename[260]; + uint64_t size; + uint64_t last_modified; +}; + +struct DiscordEntitlement { + DiscordSnowflake id; + enum EDiscordEntitlementType type; + DiscordSnowflake sku_id; +}; + +struct DiscordSkuPrice { + uint32_t amount; + char currency[16]; +}; + +struct DiscordSku { + DiscordSnowflake id; + enum EDiscordSkuType type; + char name[256]; + struct DiscordSkuPrice price; +}; + +struct DiscordInputMode { + enum EDiscordInputModeType type; + char shortcut[256]; +}; + +struct DiscordUserAchievement { + DiscordSnowflake user_id; + DiscordSnowflake achievement_id; + uint8_t percent_complete; + DiscordDateTime unlocked_at; +}; + +struct IDiscordLobbyTransaction { + enum EDiscordResult(DISCORD_API* set_type)(struct IDiscordLobbyTransaction* lobby_transaction, + enum EDiscordLobbyType type); + enum EDiscordResult(DISCORD_API* set_owner)(struct IDiscordLobbyTransaction* lobby_transaction, + DiscordUserId owner_id); + enum EDiscordResult(DISCORD_API* set_capacity)( + struct IDiscordLobbyTransaction* lobby_transaction, + uint32_t capacity); + enum EDiscordResult(DISCORD_API* set_metadata)( + struct IDiscordLobbyTransaction* lobby_transaction, + DiscordMetadataKey key, + DiscordMetadataValue value); + enum EDiscordResult(DISCORD_API* delete_metadata)( + struct IDiscordLobbyTransaction* lobby_transaction, + DiscordMetadataKey key); + enum EDiscordResult(DISCORD_API* set_locked)(struct IDiscordLobbyTransaction* lobby_transaction, + bool locked); +}; + +struct IDiscordLobbyMemberTransaction { + enum EDiscordResult(DISCORD_API* set_metadata)( + struct IDiscordLobbyMemberTransaction* lobby_member_transaction, + DiscordMetadataKey key, + DiscordMetadataValue value); + enum EDiscordResult(DISCORD_API* delete_metadata)( + struct IDiscordLobbyMemberTransaction* lobby_member_transaction, + DiscordMetadataKey key); +}; + +struct IDiscordLobbySearchQuery { + enum EDiscordResult(DISCORD_API* filter)(struct IDiscordLobbySearchQuery* lobby_search_query, + DiscordMetadataKey key, + enum EDiscordLobbySearchComparison comparison, + enum EDiscordLobbySearchCast cast, + DiscordMetadataValue value); + enum EDiscordResult(DISCORD_API* sort)(struct IDiscordLobbySearchQuery* lobby_search_query, + DiscordMetadataKey key, + enum EDiscordLobbySearchCast cast, + DiscordMetadataValue value); + enum EDiscordResult(DISCORD_API* limit)(struct IDiscordLobbySearchQuery* lobby_search_query, + uint32_t limit); + enum EDiscordResult(DISCORD_API* distance)(struct IDiscordLobbySearchQuery* lobby_search_query, + enum EDiscordLobbySearchDistance distance); +}; + +typedef void* IDiscordApplicationEvents; + +struct IDiscordApplicationManager { + void(DISCORD_API* validate_or_exit)(struct IDiscordApplicationManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* get_current_locale)(struct IDiscordApplicationManager* manager, + DiscordLocale* locale); + void(DISCORD_API* get_current_branch)(struct IDiscordApplicationManager* manager, + DiscordBranch* branch); + void(DISCORD_API* get_oauth2_token)( + struct IDiscordApplicationManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + struct DiscordOAuth2Token* oauth2_token)); + void(DISCORD_API* get_ticket)(struct IDiscordApplicationManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + const char* data)); +}; + +struct IDiscordUserEvents { + void(DISCORD_API* on_current_user_update)(void* event_data); +}; + +struct IDiscordUserManager { + enum EDiscordResult(DISCORD_API* get_current_user)(struct IDiscordUserManager* manager, + struct DiscordUser* current_user); + void(DISCORD_API* get_user)(struct IDiscordUserManager* manager, + DiscordUserId user_id, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + struct DiscordUser* user)); + enum EDiscordResult(DISCORD_API* get_current_user_premium_type)( + struct IDiscordUserManager* manager, + enum EDiscordPremiumType* premium_type); + enum EDiscordResult(DISCORD_API* current_user_has_flag)(struct IDiscordUserManager* manager, + enum EDiscordUserFlag flag, + bool* has_flag); +}; + +typedef void* IDiscordImageEvents; + +struct IDiscordImageManager { + void(DISCORD_API* fetch)(struct IDiscordImageManager* manager, + struct DiscordImageHandle handle, + bool refresh, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + struct DiscordImageHandle handle_result)); + enum EDiscordResult(DISCORD_API* get_dimensions)(struct IDiscordImageManager* manager, + struct DiscordImageHandle handle, + struct DiscordImageDimensions* dimensions); + enum EDiscordResult(DISCORD_API* get_data)(struct IDiscordImageManager* manager, + struct DiscordImageHandle handle, + uint8_t* data, + uint32_t data_length); +}; + +struct IDiscordActivityEvents { + void(DISCORD_API* on_activity_join)(void* event_data, const char* secret); + void(DISCORD_API* on_activity_spectate)(void* event_data, const char* secret); + void(DISCORD_API* on_activity_join_request)(void* event_data, struct DiscordUser* user); + void(DISCORD_API* on_activity_invite)(void* event_data, + enum EDiscordActivityActionType type, + struct DiscordUser* user, + struct DiscordActivity* activity); +}; + +struct IDiscordActivityManager { + enum EDiscordResult(DISCORD_API* register_command)(struct IDiscordActivityManager* manager, + const char* command); + enum EDiscordResult(DISCORD_API* register_steam)(struct IDiscordActivityManager* manager, + uint32_t steam_id); + void(DISCORD_API* update_activity)(struct IDiscordActivityManager* manager, + struct DiscordActivity* activity, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* clear_activity)(struct IDiscordActivityManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* send_request_reply)(struct IDiscordActivityManager* manager, + DiscordUserId user_id, + enum EDiscordActivityJoinRequestReply reply, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* send_invite)(struct IDiscordActivityManager* manager, + DiscordUserId user_id, + enum EDiscordActivityActionType type, + const char* content, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* accept_invite)(struct IDiscordActivityManager* manager, + DiscordUserId user_id, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); +}; + +struct IDiscordRelationshipEvents { + void(DISCORD_API* on_refresh)(void* event_data); + void(DISCORD_API* on_relationship_update)(void* event_data, + struct DiscordRelationship* relationship); +}; + +struct IDiscordRelationshipManager { + void(DISCORD_API* filter)(struct IDiscordRelationshipManager* manager, + void* filter_data, + bool(DISCORD_API* filter)(void* filter_data, + struct DiscordRelationship* relationship)); + enum EDiscordResult(DISCORD_API* count)(struct IDiscordRelationshipManager* manager, + int32_t* count); + enum EDiscordResult(DISCORD_API* get)(struct IDiscordRelationshipManager* manager, + DiscordUserId user_id, + struct DiscordRelationship* relationship); + enum EDiscordResult(DISCORD_API* get_at)(struct IDiscordRelationshipManager* manager, + uint32_t index, + struct DiscordRelationship* relationship); +}; + +struct IDiscordLobbyEvents { + void(DISCORD_API* on_lobby_update)(void* event_data, int64_t lobby_id); + void(DISCORD_API* on_lobby_delete)(void* event_data, int64_t lobby_id, uint32_t reason); + void(DISCORD_API* on_member_connect)(void* event_data, int64_t lobby_id, int64_t user_id); + void(DISCORD_API* on_member_update)(void* event_data, int64_t lobby_id, int64_t user_id); + void(DISCORD_API* on_member_disconnect)(void* event_data, int64_t lobby_id, int64_t user_id); + void(DISCORD_API* on_lobby_message)(void* event_data, + int64_t lobby_id, + int64_t user_id, + uint8_t* data, + uint32_t data_length); + void(DISCORD_API* on_speaking)(void* event_data, + int64_t lobby_id, + int64_t user_id, + bool speaking); + void(DISCORD_API* on_network_message)(void* event_data, + int64_t lobby_id, + int64_t user_id, + uint8_t channel_id, + uint8_t* data, + uint32_t data_length); +}; + +struct IDiscordLobbyManager { + enum EDiscordResult(DISCORD_API* get_lobby_create_transaction)( + struct IDiscordLobbyManager* manager, + struct IDiscordLobbyTransaction** transaction); + enum EDiscordResult(DISCORD_API* get_lobby_update_transaction)( + struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + struct IDiscordLobbyTransaction** transaction); + enum EDiscordResult(DISCORD_API* get_member_update_transaction)( + struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordUserId user_id, + struct IDiscordLobbyMemberTransaction** transaction); + void(DISCORD_API* create_lobby)(struct IDiscordLobbyManager* manager, + struct IDiscordLobbyTransaction* transaction, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + struct DiscordLobby* lobby)); + void(DISCORD_API* update_lobby)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + struct IDiscordLobbyTransaction* transaction, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* delete_lobby)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* connect_lobby)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordLobbySecret secret, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + struct DiscordLobby* lobby)); + void(DISCORD_API* connect_lobby_with_activity_secret)( + struct IDiscordLobbyManager* manager, + DiscordLobbySecret activity_secret, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + struct DiscordLobby* lobby)); + void(DISCORD_API* disconnect_lobby)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + enum EDiscordResult(DISCORD_API* get_lobby)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + struct DiscordLobby* lobby); + enum EDiscordResult(DISCORD_API* get_lobby_activity_secret)( + struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordLobbySecret* secret); + enum EDiscordResult(DISCORD_API* get_lobby_metadata_value)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordMetadataKey key, + DiscordMetadataValue* value); + enum EDiscordResult(DISCORD_API* get_lobby_metadata_key)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + int32_t index, + DiscordMetadataKey* key); + enum EDiscordResult(DISCORD_API* lobby_metadata_count)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + int32_t* count); + enum EDiscordResult(DISCORD_API* member_count)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + int32_t* count); + enum EDiscordResult(DISCORD_API* get_member_user_id)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + int32_t index, + DiscordUserId* user_id); + enum EDiscordResult(DISCORD_API* get_member_user)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordUserId user_id, + struct DiscordUser* user); + enum EDiscordResult(DISCORD_API* get_member_metadata_value)( + struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordUserId user_id, + DiscordMetadataKey key, + DiscordMetadataValue* value); + enum EDiscordResult(DISCORD_API* get_member_metadata_key)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordUserId user_id, + int32_t index, + DiscordMetadataKey* key); + enum EDiscordResult(DISCORD_API* member_metadata_count)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordUserId user_id, + int32_t* count); + void(DISCORD_API* update_member)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordUserId user_id, + struct IDiscordLobbyMemberTransaction* transaction, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* send_lobby_message)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + uint8_t* data, + uint32_t data_length, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + enum EDiscordResult(DISCORD_API* get_search_query)(struct IDiscordLobbyManager* manager, + struct IDiscordLobbySearchQuery** query); + void(DISCORD_API* search)(struct IDiscordLobbyManager* manager, + struct IDiscordLobbySearchQuery* query, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* lobby_count)(struct IDiscordLobbyManager* manager, int32_t* count); + enum EDiscordResult(DISCORD_API* get_lobby_id)(struct IDiscordLobbyManager* manager, + int32_t index, + DiscordLobbyId* lobby_id); + void(DISCORD_API* connect_voice)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* disconnect_voice)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + enum EDiscordResult(DISCORD_API* connect_network)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id); + enum EDiscordResult(DISCORD_API* disconnect_network)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id); + enum EDiscordResult(DISCORD_API* flush_network)(struct IDiscordLobbyManager* manager); + enum EDiscordResult(DISCORD_API* open_network_channel)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + uint8_t channel_id, + bool reliable); + enum EDiscordResult(DISCORD_API* send_network_message)(struct IDiscordLobbyManager* manager, + DiscordLobbyId lobby_id, + DiscordUserId user_id, + uint8_t channel_id, + uint8_t* data, + uint32_t data_length); +}; + +struct IDiscordNetworkEvents { + void(DISCORD_API* on_message)(void* event_data, + DiscordNetworkPeerId peer_id, + DiscordNetworkChannelId channel_id, + uint8_t* data, + uint32_t data_length); + void(DISCORD_API* on_route_update)(void* event_data, const char* route_data); +}; + +struct IDiscordNetworkManager { + /** + * Get the local peer ID for this process. + */ + void(DISCORD_API* get_peer_id)(struct IDiscordNetworkManager* manager, + DiscordNetworkPeerId* peer_id); + /** + * Send pending network messages. + */ + enum EDiscordResult(DISCORD_API* flush)(struct IDiscordNetworkManager* manager); + /** + * Open a connection to a remote peer. + */ + enum EDiscordResult(DISCORD_API* open_peer)(struct IDiscordNetworkManager* manager, + DiscordNetworkPeerId peer_id, + const char* route_data); + /** + * Update the route data for a connected peer. + */ + enum EDiscordResult(DISCORD_API* update_peer)(struct IDiscordNetworkManager* manager, + DiscordNetworkPeerId peer_id, + const char* route_data); + /** + * Close the connection to a remote peer. + */ + enum EDiscordResult(DISCORD_API* close_peer)(struct IDiscordNetworkManager* manager, + DiscordNetworkPeerId peer_id); + /** + * Open a message channel to a connected peer. + */ + enum EDiscordResult(DISCORD_API* open_channel)(struct IDiscordNetworkManager* manager, + DiscordNetworkPeerId peer_id, + DiscordNetworkChannelId channel_id, + bool reliable); + /** + * Close a message channel to a connected peer. + */ + enum EDiscordResult(DISCORD_API* close_channel)(struct IDiscordNetworkManager* manager, + DiscordNetworkPeerId peer_id, + DiscordNetworkChannelId channel_id); + /** + * Send a message to a connected peer over an opened message channel. + */ + enum EDiscordResult(DISCORD_API* send_message)(struct IDiscordNetworkManager* manager, + DiscordNetworkPeerId peer_id, + DiscordNetworkChannelId channel_id, + uint8_t* data, + uint32_t data_length); +}; + +struct IDiscordOverlayEvents { + void(DISCORD_API* on_toggle)(void* event_data, bool locked); +}; + +struct IDiscordOverlayManager { + void(DISCORD_API* is_enabled)(struct IDiscordOverlayManager* manager, bool* enabled); + void(DISCORD_API* is_locked)(struct IDiscordOverlayManager* manager, bool* locked); + void(DISCORD_API* set_locked)(struct IDiscordOverlayManager* manager, + bool locked, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* open_activity_invite)( + struct IDiscordOverlayManager* manager, + enum EDiscordActivityActionType type, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, enum EDiscordResult result)); + void(DISCORD_API* open_guild_invite)(struct IDiscordOverlayManager* manager, + const char* code, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* open_voice_settings)(struct IDiscordOverlayManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + enum EDiscordResult(DISCORD_API* init_drawing_dxgi)(struct IDiscordOverlayManager* manager, + IDXGISwapChain* swapchain, + bool use_message_forwarding); + void(DISCORD_API* on_present)(struct IDiscordOverlayManager* manager); + void(DISCORD_API* forward_message)(struct IDiscordOverlayManager* manager, MSG* message); + void(DISCORD_API* key_event)(struct IDiscordOverlayManager* manager, + bool down, + const char* key_code, + enum EDiscordKeyVariant variant); + void(DISCORD_API* char_event)(struct IDiscordOverlayManager* manager, const char* character); + void(DISCORD_API* mouse_button_event)(struct IDiscordOverlayManager* manager, + uint8_t down, + int32_t click_count, + enum EDiscordMouseButton which, + int32_t x, + int32_t y); + void(DISCORD_API* mouse_motion_event)(struct IDiscordOverlayManager* manager, + int32_t x, + int32_t y); + void(DISCORD_API* ime_commit_text)(struct IDiscordOverlayManager* manager, const char* text); + void(DISCORD_API* ime_set_composition)(struct IDiscordOverlayManager* manager, + const char* text, + struct DiscordImeUnderline* underlines, + uint32_t underlines_length, + int32_t from, + int32_t to); + void(DISCORD_API* ime_cancel_composition)(struct IDiscordOverlayManager* manager); + void(DISCORD_API* set_ime_composition_range_callback)( + struct IDiscordOverlayManager* manager, + void* on_ime_composition_range_changed_data, + void(DISCORD_API* on_ime_composition_range_changed)( + void* on_ime_composition_range_changed_data, + int32_t from, + int32_t to, + struct DiscordRect* bounds, + uint32_t bounds_length)); + void(DISCORD_API* set_ime_selection_bounds_callback)( + struct IDiscordOverlayManager* manager, + void* on_ime_selection_bounds_changed_data, + void(DISCORD_API* on_ime_selection_bounds_changed)(void* on_ime_selection_bounds_changed_data, + struct DiscordRect anchor, + struct DiscordRect focus, + bool is_anchor_first)); + bool(DISCORD_API* is_point_inside_click_zone)(struct IDiscordOverlayManager* manager, + int32_t x, + int32_t y); +}; + +typedef void* IDiscordStorageEvents; + +struct IDiscordStorageManager { + enum EDiscordResult(DISCORD_API* read)(struct IDiscordStorageManager* manager, + const char* name, + uint8_t* data, + uint32_t data_length, + uint32_t* read); + void(DISCORD_API* read_async)(struct IDiscordStorageManager* manager, + const char* name, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + uint8_t* data, + uint32_t data_length)); + void(DISCORD_API* read_async_partial)(struct IDiscordStorageManager* manager, + const char* name, + uint64_t offset, + uint64_t length, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result, + uint8_t* data, + uint32_t data_length)); + enum EDiscordResult(DISCORD_API* write)(struct IDiscordStorageManager* manager, + const char* name, + uint8_t* data, + uint32_t data_length); + void(DISCORD_API* write_async)(struct IDiscordStorageManager* manager, + const char* name, + uint8_t* data, + uint32_t data_length, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + enum EDiscordResult(DISCORD_API* delete_)(struct IDiscordStorageManager* manager, + const char* name); + enum EDiscordResult(DISCORD_API* exists)(struct IDiscordStorageManager* manager, + const char* name, + bool* exists); + void(DISCORD_API* count)(struct IDiscordStorageManager* manager, int32_t* count); + enum EDiscordResult(DISCORD_API* stat)(struct IDiscordStorageManager* manager, + const char* name, + struct DiscordFileStat* stat); + enum EDiscordResult(DISCORD_API* stat_at)(struct IDiscordStorageManager* manager, + int32_t index, + struct DiscordFileStat* stat); + enum EDiscordResult(DISCORD_API* get_path)(struct IDiscordStorageManager* manager, + DiscordPath* path); +}; + +struct IDiscordStoreEvents { + void(DISCORD_API* on_entitlement_create)(void* event_data, + struct DiscordEntitlement* entitlement); + void(DISCORD_API* on_entitlement_delete)(void* event_data, + struct DiscordEntitlement* entitlement); +}; + +struct IDiscordStoreManager { + void(DISCORD_API* fetch_skus)(struct IDiscordStoreManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* count_skus)(struct IDiscordStoreManager* manager, int32_t* count); + enum EDiscordResult(DISCORD_API* get_sku)(struct IDiscordStoreManager* manager, + DiscordSnowflake sku_id, + struct DiscordSku* sku); + enum EDiscordResult(DISCORD_API* get_sku_at)(struct IDiscordStoreManager* manager, + int32_t index, + struct DiscordSku* sku); + void(DISCORD_API* fetch_entitlements)(struct IDiscordStoreManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + void(DISCORD_API* count_entitlements)(struct IDiscordStoreManager* manager, int32_t* count); + enum EDiscordResult(DISCORD_API* get_entitlement)(struct IDiscordStoreManager* manager, + DiscordSnowflake entitlement_id, + struct DiscordEntitlement* entitlement); + enum EDiscordResult(DISCORD_API* get_entitlement_at)(struct IDiscordStoreManager* manager, + int32_t index, + struct DiscordEntitlement* entitlement); + enum EDiscordResult(DISCORD_API* has_sku_entitlement)(struct IDiscordStoreManager* manager, + DiscordSnowflake sku_id, + bool* has_entitlement); + void(DISCORD_API* start_purchase)(struct IDiscordStoreManager* manager, + DiscordSnowflake sku_id, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); +}; + +struct IDiscordVoiceEvents { + void(DISCORD_API* on_settings_update)(void* event_data); +}; + +struct IDiscordVoiceManager { + enum EDiscordResult(DISCORD_API* get_input_mode)(struct IDiscordVoiceManager* manager, + struct DiscordInputMode* input_mode); + void(DISCORD_API* set_input_mode)(struct IDiscordVoiceManager* manager, + struct DiscordInputMode input_mode, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, + enum EDiscordResult result)); + enum EDiscordResult(DISCORD_API* is_self_mute)(struct IDiscordVoiceManager* manager, + bool* mute); + enum EDiscordResult(DISCORD_API* set_self_mute)(struct IDiscordVoiceManager* manager, + bool mute); + enum EDiscordResult(DISCORD_API* is_self_deaf)(struct IDiscordVoiceManager* manager, + bool* deaf); + enum EDiscordResult(DISCORD_API* set_self_deaf)(struct IDiscordVoiceManager* manager, + bool deaf); + enum EDiscordResult(DISCORD_API* is_local_mute)(struct IDiscordVoiceManager* manager, + DiscordSnowflake user_id, + bool* mute); + enum EDiscordResult(DISCORD_API* set_local_mute)(struct IDiscordVoiceManager* manager, + DiscordSnowflake user_id, + bool mute); + enum EDiscordResult(DISCORD_API* get_local_volume)(struct IDiscordVoiceManager* manager, + DiscordSnowflake user_id, + uint8_t* volume); + enum EDiscordResult(DISCORD_API* set_local_volume)(struct IDiscordVoiceManager* manager, + DiscordSnowflake user_id, + uint8_t volume); +}; + +struct IDiscordAchievementEvents { + void(DISCORD_API* on_user_achievement_update)(void* event_data, + struct DiscordUserAchievement* user_achievement); +}; + +struct IDiscordAchievementManager { + void(DISCORD_API* set_user_achievement)( + struct IDiscordAchievementManager* manager, + DiscordSnowflake achievement_id, + uint8_t percent_complete, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, enum EDiscordResult result)); + void(DISCORD_API* fetch_user_achievements)( + struct IDiscordAchievementManager* manager, + void* callback_data, + void(DISCORD_API* callback)(void* callback_data, enum EDiscordResult result)); + void(DISCORD_API* count_user_achievements)(struct IDiscordAchievementManager* manager, + int32_t* count); + enum EDiscordResult(DISCORD_API* get_user_achievement)( + struct IDiscordAchievementManager* manager, + DiscordSnowflake user_achievement_id, + struct DiscordUserAchievement* user_achievement); + enum EDiscordResult(DISCORD_API* get_user_achievement_at)( + struct IDiscordAchievementManager* manager, + int32_t index, + struct DiscordUserAchievement* user_achievement); +}; + +typedef void* IDiscordCoreEvents; + +struct IDiscordCore { + void(DISCORD_API* destroy)(struct IDiscordCore* core); + enum EDiscordResult(DISCORD_API* run_callbacks)(struct IDiscordCore* core); + void(DISCORD_API* set_log_hook)(struct IDiscordCore* core, + enum EDiscordLogLevel min_level, + void* hook_data, + void(DISCORD_API* hook)(void* hook_data, + enum EDiscordLogLevel level, + const char* message)); + struct IDiscordApplicationManager*(DISCORD_API* get_application_manager)( + struct IDiscordCore* core); + struct IDiscordUserManager*(DISCORD_API* get_user_manager)(struct IDiscordCore* core); + struct IDiscordImageManager*(DISCORD_API* get_image_manager)(struct IDiscordCore* core); + struct IDiscordActivityManager*(DISCORD_API* get_activity_manager)(struct IDiscordCore* core); + struct IDiscordRelationshipManager*(DISCORD_API* get_relationship_manager)( + struct IDiscordCore* core); + struct IDiscordLobbyManager*(DISCORD_API* get_lobby_manager)(struct IDiscordCore* core); + struct IDiscordNetworkManager*(DISCORD_API* get_network_manager)(struct IDiscordCore* core); + struct IDiscordOverlayManager*(DISCORD_API* get_overlay_manager)(struct IDiscordCore* core); + struct IDiscordStorageManager*(DISCORD_API* get_storage_manager)(struct IDiscordCore* core); + struct IDiscordStoreManager*(DISCORD_API* get_store_manager)(struct IDiscordCore* core); + struct IDiscordVoiceManager*(DISCORD_API* get_voice_manager)(struct IDiscordCore* core); + struct IDiscordAchievementManager*(DISCORD_API* get_achievement_manager)( + struct IDiscordCore* core); +}; + +struct DiscordCreateParams { + DiscordClientId client_id; + uint64_t flags; + IDiscordCoreEvents* events; + void* event_data; + IDiscordApplicationEvents* application_events; + DiscordVersion application_version; + struct IDiscordUserEvents* user_events; + DiscordVersion user_version; + IDiscordImageEvents* image_events; + DiscordVersion image_version; + struct IDiscordActivityEvents* activity_events; + DiscordVersion activity_version; + struct IDiscordRelationshipEvents* relationship_events; + DiscordVersion relationship_version; + struct IDiscordLobbyEvents* lobby_events; + DiscordVersion lobby_version; + struct IDiscordNetworkEvents* network_events; + DiscordVersion network_version; + struct IDiscordOverlayEvents* overlay_events; + DiscordVersion overlay_version; + IDiscordStorageEvents* storage_events; + DiscordVersion storage_version; + struct IDiscordStoreEvents* store_events; + DiscordVersion store_version; + struct IDiscordVoiceEvents* voice_events; + DiscordVersion voice_version; + struct IDiscordAchievementEvents* achievement_events; + DiscordVersion achievement_version; +}; + +#ifdef __cplusplus +inline +#else +static +#endif + void + DiscordCreateParamsSetDefault(struct DiscordCreateParams* params) +{ + memset(params, 0, sizeof(struct DiscordCreateParams)); + params->application_version = DISCORD_APPLICATION_MANAGER_VERSION; + params->user_version = DISCORD_USER_MANAGER_VERSION; + params->image_version = DISCORD_IMAGE_MANAGER_VERSION; + params->activity_version = DISCORD_ACTIVITY_MANAGER_VERSION; + params->relationship_version = DISCORD_RELATIONSHIP_MANAGER_VERSION; + params->lobby_version = DISCORD_LOBBY_MANAGER_VERSION; + params->network_version = DISCORD_NETWORK_MANAGER_VERSION; + params->overlay_version = DISCORD_OVERLAY_MANAGER_VERSION; + params->storage_version = DISCORD_STORAGE_MANAGER_VERSION; + params->store_version = DISCORD_STORE_MANAGER_VERSION; + params->voice_version = DISCORD_VOICE_MANAGER_VERSION; + params->achievement_version = DISCORD_ACHIEVEMENT_MANAGER_VERSION; +} + +enum EDiscordResult DISCORD_API DiscordCreate(DiscordVersion version, + struct DiscordCreateParams* params, + struct IDiscordCore** result); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Crawler/discord-files/image_manager.cpp b/Crawler/discord-files/image_manager.cpp new file mode 100644 index 00000000..03b1db49 --- /dev/null +++ b/Crawler/discord-files/image_manager.cpp @@ -0,0 +1,57 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "image_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +void ImageManager::Fetch(ImageHandle handle, + bool refresh, + std::function callback) +{ + static auto wrapper = + [](void* callbackData, EDiscordResult result, DiscordImageHandle handleResult) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), *reinterpret_cast(&handleResult)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->fetch(internal_, + *reinterpret_cast(&handle), + (refresh ? 1 : 0), + cb.release(), + wrapper); +} + +Result ImageManager::GetDimensions(ImageHandle handle, ImageDimensions* dimensions) +{ + if (!dimensions) { + return Result::InternalError; + } + + auto result = internal_->get_dimensions(internal_, + *reinterpret_cast(&handle), + reinterpret_cast(dimensions)); + return static_cast(result); +} + +Result ImageManager::GetData(ImageHandle handle, std::uint8_t* data, std::uint32_t dataLength) +{ + auto result = internal_->get_data(internal_, + *reinterpret_cast(&handle), + reinterpret_cast(data), + dataLength); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/image_manager.h b/Crawler/discord-files/image_manager.h new file mode 100644 index 00000000..b096b171 --- /dev/null +++ b/Crawler/discord-files/image_manager.h @@ -0,0 +1,28 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class ImageManager final { +public: + ~ImageManager() = default; + + void Fetch(ImageHandle handle, bool refresh, std::function callback); + Result GetDimensions(ImageHandle handle, ImageDimensions* dimensions); + Result GetData(ImageHandle handle, std::uint8_t* data, std::uint32_t dataLength); + +private: + friend class Core; + + ImageManager() = default; + ImageManager(ImageManager const& rhs) = delete; + ImageManager& operator=(ImageManager const& rhs) = delete; + ImageManager(ImageManager&& rhs) = delete; + ImageManager& operator=(ImageManager&& rhs) = delete; + + IDiscordImageManager* internal_; + static IDiscordImageEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/lobby_manager.cpp b/Crawler/discord-files/lobby_manager.cpp new file mode 100644 index 00000000..3a95b1a0 --- /dev/null +++ b/Crawler/discord-files/lobby_manager.cpp @@ -0,0 +1,554 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "lobby_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class LobbyEvents final { +public: + static void DISCORD_CALLBACK OnLobbyUpdate(void* callbackData, int64_t lobbyId) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnLobbyUpdate(lobbyId); + } + + static void DISCORD_CALLBACK OnLobbyDelete(void* callbackData, int64_t lobbyId, uint32_t reason) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnLobbyDelete(lobbyId, reason); + } + + static void DISCORD_CALLBACK OnMemberConnect(void* callbackData, + int64_t lobbyId, + int64_t userId) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnMemberConnect(lobbyId, userId); + } + + static void DISCORD_CALLBACK OnMemberUpdate(void* callbackData, int64_t lobbyId, int64_t userId) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnMemberUpdate(lobbyId, userId); + } + + static void DISCORD_CALLBACK OnMemberDisconnect(void* callbackData, + int64_t lobbyId, + int64_t userId) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnMemberDisconnect(lobbyId, userId); + } + + static void DISCORD_CALLBACK OnLobbyMessage(void* callbackData, + int64_t lobbyId, + int64_t userId, + uint8_t* data, + uint32_t dataLength) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnLobbyMessage(lobbyId, userId, data, dataLength); + } + + static void DISCORD_CALLBACK OnSpeaking(void* callbackData, + int64_t lobbyId, + int64_t userId, + bool speaking) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnSpeaking(lobbyId, userId, (speaking != 0)); + } + + static void DISCORD_CALLBACK OnNetworkMessage(void* callbackData, + int64_t lobbyId, + int64_t userId, + uint8_t channelId, + uint8_t* data, + uint32_t dataLength) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->LobbyManager(); + module.OnNetworkMessage(lobbyId, userId, channelId, data, dataLength); + } +}; + +IDiscordLobbyEvents LobbyManager::events_{ + &LobbyEvents::OnLobbyUpdate, + &LobbyEvents::OnLobbyDelete, + &LobbyEvents::OnMemberConnect, + &LobbyEvents::OnMemberUpdate, + &LobbyEvents::OnMemberDisconnect, + &LobbyEvents::OnLobbyMessage, + &LobbyEvents::OnSpeaking, + &LobbyEvents::OnNetworkMessage, +}; + +Result LobbyManager::GetLobbyCreateTransaction(LobbyTransaction* transaction) +{ + if (!transaction) { + return Result::InternalError; + } + + auto result = internal_->get_lobby_create_transaction(internal_, transaction->Receive()); + return static_cast(result); +} + +Result LobbyManager::GetLobbyUpdateTransaction(LobbyId lobbyId, LobbyTransaction* transaction) +{ + if (!transaction) { + return Result::InternalError; + } + + auto result = + internal_->get_lobby_update_transaction(internal_, lobbyId, transaction->Receive()); + return static_cast(result); +} + +Result LobbyManager::GetMemberUpdateTransaction(LobbyId lobbyId, + UserId userId, + LobbyMemberTransaction* transaction) +{ + if (!transaction) { + return Result::InternalError; + } + + auto result = + internal_->get_member_update_transaction(internal_, lobbyId, userId, transaction->Receive()); + return static_cast(result); +} + +void LobbyManager::CreateLobby(LobbyTransaction const& transaction, + std::function callback) +{ + static auto wrapper = + [](void* callbackData, EDiscordResult result, DiscordLobby* lobby) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), *reinterpret_cast(lobby)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->create_lobby( + internal_, const_cast(transaction).Internal(), cb.release(), wrapper); +} + +void LobbyManager::UpdateLobby(LobbyId lobbyId, + LobbyTransaction const& transaction, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->update_lobby(internal_, + lobbyId, + const_cast(transaction).Internal(), + cb.release(), + wrapper); +} + +void LobbyManager::DeleteLobby(LobbyId lobbyId, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->delete_lobby(internal_, lobbyId, cb.release(), wrapper); +} + +void LobbyManager::ConnectLobby(LobbyId lobbyId, + LobbySecret secret, + std::function callback) +{ + static auto wrapper = + [](void* callbackData, EDiscordResult result, DiscordLobby* lobby) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), *reinterpret_cast(lobby)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->connect_lobby(internal_, lobbyId, const_cast(secret), cb.release(), wrapper); +} + +void LobbyManager::ConnectLobbyWithActivitySecret( + LobbySecret activitySecret, + std::function callback) +{ + static auto wrapper = + [](void* callbackData, EDiscordResult result, DiscordLobby* lobby) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), *reinterpret_cast(lobby)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->connect_lobby_with_activity_secret( + internal_, const_cast(activitySecret), cb.release(), wrapper); +} + +void LobbyManager::DisconnectLobby(LobbyId lobbyId, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->disconnect_lobby(internal_, lobbyId, cb.release(), wrapper); +} + +Result LobbyManager::GetLobby(LobbyId lobbyId, Lobby* lobby) +{ + if (!lobby) { + return Result::InternalError; + } + + auto result = internal_->get_lobby(internal_, lobbyId, reinterpret_cast(lobby)); + return static_cast(result); +} + +Result LobbyManager::GetLobbyActivitySecret(LobbyId lobbyId, char secret[128]) +{ + if (!secret) { + return Result::InternalError; + } + + auto result = internal_->get_lobby_activity_secret( + internal_, lobbyId, reinterpret_cast(secret)); + return static_cast(result); +} + +Result LobbyManager::GetLobbyMetadataValue(LobbyId lobbyId, MetadataKey key, char value[4096]) +{ + if (!value) { + return Result::InternalError; + } + + auto result = internal_->get_lobby_metadata_value( + internal_, lobbyId, const_cast(key), reinterpret_cast(value)); + return static_cast(result); +} + +Result LobbyManager::GetLobbyMetadataKey(LobbyId lobbyId, std::int32_t index, char key[256]) +{ + if (!key) { + return Result::InternalError; + } + + auto result = internal_->get_lobby_metadata_key( + internal_, lobbyId, index, reinterpret_cast(key)); + return static_cast(result); +} + +Result LobbyManager::LobbyMetadataCount(LobbyId lobbyId, std::int32_t* count) +{ + if (!count) { + return Result::InternalError; + } + + auto result = + internal_->lobby_metadata_count(internal_, lobbyId, reinterpret_cast(count)); + return static_cast(result); +} + +Result LobbyManager::MemberCount(LobbyId lobbyId, std::int32_t* count) +{ + if (!count) { + return Result::InternalError; + } + + auto result = internal_->member_count(internal_, lobbyId, reinterpret_cast(count)); + return static_cast(result); +} + +Result LobbyManager::GetMemberUserId(LobbyId lobbyId, std::int32_t index, UserId* userId) +{ + if (!userId) { + return Result::InternalError; + } + + auto result = + internal_->get_member_user_id(internal_, lobbyId, index, reinterpret_cast(userId)); + return static_cast(result); +} + +Result LobbyManager::GetMemberUser(LobbyId lobbyId, UserId userId, User* user) +{ + if (!user) { + return Result::InternalError; + } + + auto result = + internal_->get_member_user(internal_, lobbyId, userId, reinterpret_cast(user)); + return static_cast(result); +} + +Result LobbyManager::GetMemberMetadataValue(LobbyId lobbyId, + UserId userId, + MetadataKey key, + char value[4096]) +{ + if (!value) { + return Result::InternalError; + } + + auto result = + internal_->get_member_metadata_value(internal_, + lobbyId, + userId, + const_cast(key), + reinterpret_cast(value)); + return static_cast(result); +} + +Result LobbyManager::GetMemberMetadataKey(LobbyId lobbyId, + UserId userId, + std::int32_t index, + char key[256]) +{ + if (!key) { + return Result::InternalError; + } + + auto result = internal_->get_member_metadata_key( + internal_, lobbyId, userId, index, reinterpret_cast(key)); + return static_cast(result); +} + +Result LobbyManager::MemberMetadataCount(LobbyId lobbyId, UserId userId, std::int32_t* count) +{ + if (!count) { + return Result::InternalError; + } + + auto result = internal_->member_metadata_count( + internal_, lobbyId, userId, reinterpret_cast(count)); + return static_cast(result); +} + +void LobbyManager::UpdateMember(LobbyId lobbyId, + UserId userId, + LobbyMemberTransaction const& transaction, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->update_member(internal_, + lobbyId, + userId, + const_cast(transaction).Internal(), + cb.release(), + wrapper); +} + +void LobbyManager::SendLobbyMessage(LobbyId lobbyId, + std::uint8_t* data, + std::uint32_t dataLength, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->send_lobby_message( + internal_, lobbyId, reinterpret_cast(data), dataLength, cb.release(), wrapper); +} + +Result LobbyManager::GetSearchQuery(LobbySearchQuery* query) +{ + if (!query) { + return Result::InternalError; + } + + auto result = internal_->get_search_query(internal_, query->Receive()); + return static_cast(result); +} + +void LobbyManager::Search(LobbySearchQuery const& query, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->search( + internal_, const_cast(query).Internal(), cb.release(), wrapper); +} + +void LobbyManager::LobbyCount(std::int32_t* count) +{ + if (!count) { + return; + } + + internal_->lobby_count(internal_, reinterpret_cast(count)); +} + +Result LobbyManager::GetLobbyId(std::int32_t index, LobbyId* lobbyId) +{ + if (!lobbyId) { + return Result::InternalError; + } + + auto result = internal_->get_lobby_id(internal_, index, reinterpret_cast(lobbyId)); + return static_cast(result); +} + +void LobbyManager::ConnectVoice(LobbyId lobbyId, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->connect_voice(internal_, lobbyId, cb.release(), wrapper); +} + +void LobbyManager::DisconnectVoice(LobbyId lobbyId, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->disconnect_voice(internal_, lobbyId, cb.release(), wrapper); +} + +Result LobbyManager::ConnectNetwork(LobbyId lobbyId) +{ + auto result = internal_->connect_network(internal_, lobbyId); + return static_cast(result); +} + +Result LobbyManager::DisconnectNetwork(LobbyId lobbyId) +{ + auto result = internal_->disconnect_network(internal_, lobbyId); + return static_cast(result); +} + +Result LobbyManager::FlushNetwork() +{ + auto result = internal_->flush_network(internal_); + return static_cast(result); +} + +Result LobbyManager::OpenNetworkChannel(LobbyId lobbyId, std::uint8_t channelId, bool reliable) +{ + auto result = + internal_->open_network_channel(internal_, lobbyId, channelId, (reliable ? 1 : 0)); + return static_cast(result); +} + +Result LobbyManager::SendNetworkMessage(LobbyId lobbyId, + UserId userId, + std::uint8_t channelId, + std::uint8_t* data, + std::uint32_t dataLength) +{ + auto result = internal_->send_network_message( + internal_, lobbyId, userId, channelId, reinterpret_cast(data), dataLength); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/lobby_manager.h b/Crawler/discord-files/lobby_manager.h new file mode 100644 index 00000000..96380cbf --- /dev/null +++ b/Crawler/discord-files/lobby_manager.h @@ -0,0 +1,88 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class LobbyManager final { +public: + ~LobbyManager() = default; + + Result GetLobbyCreateTransaction(LobbyTransaction* transaction); + Result GetLobbyUpdateTransaction(LobbyId lobbyId, LobbyTransaction* transaction); + Result GetMemberUpdateTransaction(LobbyId lobbyId, + UserId userId, + LobbyMemberTransaction* transaction); + void CreateLobby(LobbyTransaction const& transaction, + std::function callback); + void UpdateLobby(LobbyId lobbyId, + LobbyTransaction const& transaction, + std::function callback); + void DeleteLobby(LobbyId lobbyId, std::function callback); + void ConnectLobby(LobbyId lobbyId, + LobbySecret secret, + std::function callback); + void ConnectLobbyWithActivitySecret(LobbySecret activitySecret, + std::function callback); + void DisconnectLobby(LobbyId lobbyId, std::function callback); + Result GetLobby(LobbyId lobbyId, Lobby* lobby); + Result GetLobbyActivitySecret(LobbyId lobbyId, char secret[128]); + Result GetLobbyMetadataValue(LobbyId lobbyId, MetadataKey key, char value[4096]); + Result GetLobbyMetadataKey(LobbyId lobbyId, std::int32_t index, char key[256]); + Result LobbyMetadataCount(LobbyId lobbyId, std::int32_t* count); + Result MemberCount(LobbyId lobbyId, std::int32_t* count); + Result GetMemberUserId(LobbyId lobbyId, std::int32_t index, UserId* userId); + Result GetMemberUser(LobbyId lobbyId, UserId userId, User* user); + Result GetMemberMetadataValue(LobbyId lobbyId, + UserId userId, + MetadataKey key, + char value[4096]); + Result GetMemberMetadataKey(LobbyId lobbyId, UserId userId, std::int32_t index, char key[256]); + Result MemberMetadataCount(LobbyId lobbyId, UserId userId, std::int32_t* count); + void UpdateMember(LobbyId lobbyId, + UserId userId, + LobbyMemberTransaction const& transaction, + std::function callback); + void SendLobbyMessage(LobbyId lobbyId, + std::uint8_t* data, + std::uint32_t dataLength, + std::function callback); + Result GetSearchQuery(LobbySearchQuery* query); + void Search(LobbySearchQuery const& query, std::function callback); + void LobbyCount(std::int32_t* count); + Result GetLobbyId(std::int32_t index, LobbyId* lobbyId); + void ConnectVoice(LobbyId lobbyId, std::function callback); + void DisconnectVoice(LobbyId lobbyId, std::function callback); + Result ConnectNetwork(LobbyId lobbyId); + Result DisconnectNetwork(LobbyId lobbyId); + Result FlushNetwork(); + Result OpenNetworkChannel(LobbyId lobbyId, std::uint8_t channelId, bool reliable); + Result SendNetworkMessage(LobbyId lobbyId, + UserId userId, + std::uint8_t channelId, + std::uint8_t* data, + std::uint32_t dataLength); + + Event OnLobbyUpdate; + Event OnLobbyDelete; + Event OnMemberConnect; + Event OnMemberUpdate; + Event OnMemberDisconnect; + Event OnLobbyMessage; + Event OnSpeaking; + Event OnNetworkMessage; + +private: + friend class Core; + + LobbyManager() = default; + LobbyManager(LobbyManager const& rhs) = delete; + LobbyManager& operator=(LobbyManager const& rhs) = delete; + LobbyManager(LobbyManager&& rhs) = delete; + LobbyManager& operator=(LobbyManager&& rhs) = delete; + + IDiscordLobbyManager* internal_; + static IDiscordLobbyEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/network_manager.cpp b/Crawler/discord-files/network_manager.cpp new file mode 100644 index 00000000..36031b32 --- /dev/null +++ b/Crawler/discord-files/network_manager.cpp @@ -0,0 +1,103 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "network_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class NetworkEvents final { +public: + static void DISCORD_CALLBACK OnMessage(void* callbackData, + DiscordNetworkPeerId peerId, + DiscordNetworkChannelId channelId, + uint8_t* data, + uint32_t dataLength) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->NetworkManager(); + module.OnMessage(peerId, channelId, data, dataLength); + } + + static void DISCORD_CALLBACK OnRouteUpdate(void* callbackData, char const* routeData) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->NetworkManager(); + module.OnRouteUpdate(static_cast(routeData)); + } +}; + +IDiscordNetworkEvents NetworkManager::events_{ + &NetworkEvents::OnMessage, + &NetworkEvents::OnRouteUpdate, +}; + +void NetworkManager::GetPeerId(NetworkPeerId* peerId) +{ + if (!peerId) { + return; + } + + internal_->get_peer_id(internal_, reinterpret_cast(peerId)); +} + +Result NetworkManager::Flush() +{ + auto result = internal_->flush(internal_); + return static_cast(result); +} + +Result NetworkManager::OpenPeer(NetworkPeerId peerId, char const* routeData) +{ + auto result = internal_->open_peer(internal_, peerId, const_cast(routeData)); + return static_cast(result); +} + +Result NetworkManager::UpdatePeer(NetworkPeerId peerId, char const* routeData) +{ + auto result = internal_->update_peer(internal_, peerId, const_cast(routeData)); + return static_cast(result); +} + +Result NetworkManager::ClosePeer(NetworkPeerId peerId) +{ + auto result = internal_->close_peer(internal_, peerId); + return static_cast(result); +} + +Result NetworkManager::OpenChannel(NetworkPeerId peerId, NetworkChannelId channelId, bool reliable) +{ + auto result = internal_->open_channel(internal_, peerId, channelId, (reliable ? 1 : 0)); + return static_cast(result); +} + +Result NetworkManager::CloseChannel(NetworkPeerId peerId, NetworkChannelId channelId) +{ + auto result = internal_->close_channel(internal_, peerId, channelId); + return static_cast(result); +} + +Result NetworkManager::SendMessage(NetworkPeerId peerId, + NetworkChannelId channelId, + std::uint8_t* data, + std::uint32_t dataLength) +{ + auto result = internal_->send_message( + internal_, peerId, channelId, reinterpret_cast(data), dataLength); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/network_manager.h b/Crawler/discord-files/network_manager.h new file mode 100644 index 00000000..e374670a --- /dev/null +++ b/Crawler/discord-files/network_manager.h @@ -0,0 +1,63 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class NetworkManager final { +public: + ~NetworkManager() = default; + + /** + * Get the local peer ID for this process. + */ + void GetPeerId(NetworkPeerId* peerId); + /** + * Send pending network messages. + */ + Result Flush(); + /** + * Open a connection to a remote peer. + */ + Result OpenPeer(NetworkPeerId peerId, char const* routeData); + /** + * Update the route data for a connected peer. + */ + Result UpdatePeer(NetworkPeerId peerId, char const* routeData); + /** + * Close the connection to a remote peer. + */ + Result ClosePeer(NetworkPeerId peerId); + /** + * Open a message channel to a connected peer. + */ + Result OpenChannel(NetworkPeerId peerId, NetworkChannelId channelId, bool reliable); + /** + * Close a message channel to a connected peer. + */ + Result CloseChannel(NetworkPeerId peerId, NetworkChannelId channelId); + /** + * Send a message to a connected peer over an opened message channel. + */ + Result SendMessage(NetworkPeerId peerId, + NetworkChannelId channelId, + std::uint8_t* data, + std::uint32_t dataLength); + + Event OnMessage; + Event OnRouteUpdate; + +private: + friend class Core; + + NetworkManager() = default; + NetworkManager(NetworkManager const& rhs) = delete; + NetworkManager& operator=(NetworkManager const& rhs) = delete; + NetworkManager(NetworkManager&& rhs) = delete; + NetworkManager& operator=(NetworkManager&& rhs) = delete; + + IDiscordNetworkManager* internal_; + static IDiscordNetworkEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/overlay_manager.cpp b/Crawler/discord-files/overlay_manager.cpp new file mode 100644 index 00000000..f4b1fba7 --- /dev/null +++ b/Crawler/discord-files/overlay_manager.cpp @@ -0,0 +1,229 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "overlay_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class OverlayEvents final { +public: + static void DISCORD_CALLBACK OnToggle(void* callbackData, bool locked) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->OverlayManager(); + module.OnToggle((locked != 0)); + } +}; + +IDiscordOverlayEvents OverlayManager::events_{ + &OverlayEvents::OnToggle, +}; + +void OverlayManager::IsEnabled(bool* enabled) +{ + if (!enabled) { + return; + } + + internal_->is_enabled(internal_, reinterpret_cast(enabled)); +} + +void OverlayManager::IsLocked(bool* locked) +{ + if (!locked) { + return; + } + + internal_->is_locked(internal_, reinterpret_cast(locked)); +} + +void OverlayManager::SetLocked(bool locked, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->set_locked(internal_, (locked ? 1 : 0), cb.release(), wrapper); +} + +void OverlayManager::OpenActivityInvite(ActivityActionType type, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->open_activity_invite( + internal_, static_cast(type), cb.release(), wrapper); +} + +void OverlayManager::OpenGuildInvite(char const* code, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->open_guild_invite(internal_, const_cast(code), cb.release(), wrapper); +} + +void OverlayManager::OpenVoiceSettings(std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->open_voice_settings(internal_, cb.release(), wrapper); +} + +Result OverlayManager::InitDrawingDxgi(IDXGISwapChain* swapchain, bool useMessageForwarding) +{ + auto result = + internal_->init_drawing_dxgi(internal_, swapchain, (useMessageForwarding ? 1 : 0)); + return static_cast(result); +} + +void OverlayManager::OnPresent() +{ + internal_->on_present(internal_); +} + +void OverlayManager::ForwardMessage(MSG* message) +{ + internal_->forward_message(internal_, message); +} + +void OverlayManager::KeyEvent(bool down, char const* keyCode, KeyVariant variant) +{ + internal_->key_event(internal_, + (down ? 1 : 0), + const_cast(keyCode), + static_cast(variant)); +} + +void OverlayManager::CharEvent(char const* character) +{ + internal_->char_event(internal_, const_cast(character)); +} + +void OverlayManager::MouseButtonEvent(std::uint8_t down, + std::int32_t clickCount, + MouseButton which, + std::int32_t x, + std::int32_t y) +{ + internal_->mouse_button_event( + internal_, down, clickCount, static_cast(which), x, y); +} + +void OverlayManager::MouseMotionEvent(std::int32_t x, std::int32_t y) +{ + internal_->mouse_motion_event(internal_, x, y); +} + +void OverlayManager::ImeCommitText(char const* text) +{ + internal_->ime_commit_text(internal_, const_cast(text)); +} + +void OverlayManager::ImeSetComposition(char const* text, + ImeUnderline* underlines, + std::uint32_t underlinesLength, + std::int32_t from, + std::int32_t to) +{ + internal_->ime_set_composition(internal_, + const_cast(text), + reinterpret_cast(underlines), + underlinesLength, + from, + to); +} + +void OverlayManager::ImeCancelComposition() +{ + internal_->ime_cancel_composition(internal_); +} + +void OverlayManager::SetImeCompositionRangeCallback( + std::function + onImeCompositionRangeChanged) +{ + static auto wrapper = [](void* callbackData, + int32_t from, + int32_t to, + DiscordRect* bounds, + uint32_t boundsLength) -> void { + std::unique_ptr> cb( + reinterpret_cast*>( + callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(from, to, reinterpret_cast(bounds), boundsLength); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function( + std::move(onImeCompositionRangeChanged))); + internal_->set_ime_composition_range_callback(internal_, cb.release(), wrapper); +} + +void OverlayManager::SetImeSelectionBoundsCallback( + std::function onImeSelectionBoundsChanged) +{ + static auto wrapper = + [](void* callbackData, DiscordRect anchor, DiscordRect focus, bool isAnchorFirst) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(*reinterpret_cast(&anchor), + *reinterpret_cast(&focus), + (isAnchorFirst != 0)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(onImeSelectionBoundsChanged))); + internal_->set_ime_selection_bounds_callback(internal_, cb.release(), wrapper); +} + +bool OverlayManager::IsPointInsideClickZone(std::int32_t x, std::int32_t y) +{ + auto result = internal_->is_point_inside_click_zone(internal_, x, y); + return (result != 0); +} + +} // namespace discord diff --git a/Crawler/discord-files/overlay_manager.h b/Crawler/discord-files/overlay_manager.h new file mode 100644 index 00000000..5f73a367 --- /dev/null +++ b/Crawler/discord-files/overlay_manager.h @@ -0,0 +1,57 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class OverlayManager final { +public: + ~OverlayManager() = default; + + void IsEnabled(bool* enabled); + void IsLocked(bool* locked); + void SetLocked(bool locked, std::function callback); + void OpenActivityInvite(ActivityActionType type, std::function callback); + void OpenGuildInvite(char const* code, std::function callback); + void OpenVoiceSettings(std::function callback); + Result InitDrawingDxgi(IDXGISwapChain* swapchain, bool useMessageForwarding); + void OnPresent(); + void ForwardMessage(MSG* message); + void KeyEvent(bool down, char const* keyCode, KeyVariant variant); + void CharEvent(char const* character); + void MouseButtonEvent(std::uint8_t down, + std::int32_t clickCount, + MouseButton which, + std::int32_t x, + std::int32_t y); + void MouseMotionEvent(std::int32_t x, std::int32_t y); + void ImeCommitText(char const* text); + void ImeSetComposition(char const* text, + ImeUnderline* underlines, + std::uint32_t underlinesLength, + std::int32_t from, + std::int32_t to); + void ImeCancelComposition(); + void SetImeCompositionRangeCallback( + std::function + onImeCompositionRangeChanged); + void SetImeSelectionBoundsCallback( + std::function onImeSelectionBoundsChanged); + bool IsPointInsideClickZone(std::int32_t x, std::int32_t y); + + Event OnToggle; + +private: + friend class Core; + + OverlayManager() = default; + OverlayManager(OverlayManager const& rhs) = delete; + OverlayManager& operator=(OverlayManager const& rhs) = delete; + OverlayManager(OverlayManager&& rhs) = delete; + OverlayManager& operator=(OverlayManager&& rhs) = delete; + + IDiscordOverlayManager* internal_; + static IDiscordOverlayEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/relationship_manager.cpp b/Crawler/discord-files/relationship_manager.cpp new file mode 100644 index 00000000..dce874ea --- /dev/null +++ b/Crawler/discord-files/relationship_manager.cpp @@ -0,0 +1,91 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "relationship_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class RelationshipEvents final { +public: + static void DISCORD_CALLBACK OnRefresh(void* callbackData) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->RelationshipManager(); + module.OnRefresh(); + } + + static void DISCORD_CALLBACK OnRelationshipUpdate(void* callbackData, + DiscordRelationship* relationship) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->RelationshipManager(); + module.OnRelationshipUpdate(*reinterpret_cast(relationship)); + } +}; + +IDiscordRelationshipEvents RelationshipManager::events_{ + &RelationshipEvents::OnRefresh, + &RelationshipEvents::OnRelationshipUpdate, +}; + +void RelationshipManager::Filter(std::function filter) +{ + static auto wrapper = [](void* callbackData, DiscordRelationship* relationship) -> bool { + auto cb(reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return {}; + } + return (*cb)(*reinterpret_cast(relationship)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(filter))); + internal_->filter(internal_, cb.get(), wrapper); +} + +Result RelationshipManager::Count(std::int32_t* count) +{ + if (!count) { + return Result::InternalError; + } + + auto result = internal_->count(internal_, reinterpret_cast(count)); + return static_cast(result); +} + +Result RelationshipManager::Get(UserId userId, Relationship* relationship) +{ + if (!relationship) { + return Result::InternalError; + } + + auto result = + internal_->get(internal_, userId, reinterpret_cast(relationship)); + return static_cast(result); +} + +Result RelationshipManager::GetAt(std::uint32_t index, Relationship* relationship) +{ + if (!relationship) { + return Result::InternalError; + } + + auto result = + internal_->get_at(internal_, index, reinterpret_cast(relationship)); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/relationship_manager.h b/Crawler/discord-files/relationship_manager.h new file mode 100644 index 00000000..e9cd0161 --- /dev/null +++ b/Crawler/discord-files/relationship_manager.h @@ -0,0 +1,32 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class RelationshipManager final { +public: + ~RelationshipManager() = default; + + void Filter(std::function filter); + Result Count(std::int32_t* count); + Result Get(UserId userId, Relationship* relationship); + Result GetAt(std::uint32_t index, Relationship* relationship); + + Event<> OnRefresh; + Event OnRelationshipUpdate; + +private: + friend class Core; + + RelationshipManager() = default; + RelationshipManager(RelationshipManager const& rhs) = delete; + RelationshipManager& operator=(RelationshipManager const& rhs) = delete; + RelationshipManager(RelationshipManager&& rhs) = delete; + RelationshipManager& operator=(RelationshipManager&& rhs) = delete; + + IDiscordRelationshipManager* internal_; + static IDiscordRelationshipEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/storage_manager.cpp b/Crawler/discord-files/storage_manager.cpp new file mode 100644 index 00000000..fbf9ca75 --- /dev/null +++ b/Crawler/discord-files/storage_manager.cpp @@ -0,0 +1,158 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "storage_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +Result StorageManager::Read(char const* name, + std::uint8_t* data, + std::uint32_t dataLength, + std::uint32_t* read) +{ + if (!read) { + return Result::InternalError; + } + + auto result = internal_->read(internal_, + const_cast(name), + reinterpret_cast(data), + dataLength, + reinterpret_cast(read)); + return static_cast(result); +} + +void StorageManager::ReadAsync(char const* name, + std::function callback) +{ + static auto wrapper = + [](void* callbackData, EDiscordResult result, uint8_t* data, uint32_t dataLength) -> void { + std::unique_ptr> cb( + reinterpret_cast*>( + callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), data, dataLength); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->read_async(internal_, const_cast(name), cb.release(), wrapper); +} + +void StorageManager::ReadAsyncPartial( + char const* name, + std::uint64_t offset, + std::uint64_t length, + std::function callback) +{ + static auto wrapper = + [](void* callbackData, EDiscordResult result, uint8_t* data, uint32_t dataLength) -> void { + std::unique_ptr> cb( + reinterpret_cast*>( + callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), data, dataLength); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->read_async_partial( + internal_, const_cast(name), offset, length, cb.release(), wrapper); +} + +Result StorageManager::Write(char const* name, std::uint8_t* data, std::uint32_t dataLength) +{ + auto result = internal_->write( + internal_, const_cast(name), reinterpret_cast(data), dataLength); + return static_cast(result); +} + +void StorageManager::WriteAsync(char const* name, + std::uint8_t* data, + std::uint32_t dataLength, + std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->write_async(internal_, + const_cast(name), + reinterpret_cast(data), + dataLength, + cb.release(), + wrapper); +} + +Result StorageManager::Delete(char const* name) +{ + auto result = internal_->delete_(internal_, const_cast(name)); + return static_cast(result); +} + +Result StorageManager::Exists(char const* name, bool* exists) +{ + if (!exists) { + return Result::InternalError; + } + + auto result = + internal_->exists(internal_, const_cast(name), reinterpret_cast(exists)); + return static_cast(result); +} + +void StorageManager::Count(std::int32_t* count) +{ + if (!count) { + return; + } + + internal_->count(internal_, reinterpret_cast(count)); +} + +Result StorageManager::Stat(char const* name, FileStat* stat) +{ + if (!stat) { + return Result::InternalError; + } + + auto result = + internal_->stat(internal_, const_cast(name), reinterpret_cast(stat)); + return static_cast(result); +} + +Result StorageManager::StatAt(std::int32_t index, FileStat* stat) +{ + if (!stat) { + return Result::InternalError; + } + + auto result = internal_->stat_at(internal_, index, reinterpret_cast(stat)); + return static_cast(result); +} + +Result StorageManager::GetPath(char path[4096]) +{ + if (!path) { + return Result::InternalError; + } + + auto result = internal_->get_path(internal_, reinterpret_cast(path)); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/storage_manager.h b/Crawler/discord-files/storage_manager.h new file mode 100644 index 00000000..5d6d17b1 --- /dev/null +++ b/Crawler/discord-files/storage_manager.h @@ -0,0 +1,46 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class StorageManager final { +public: + ~StorageManager() = default; + + Result Read(char const* name, + std::uint8_t* data, + std::uint32_t dataLength, + std::uint32_t* read); + void ReadAsync(char const* name, + std::function callback); + void ReadAsyncPartial(char const* name, + std::uint64_t offset, + std::uint64_t length, + std::function callback); + Result Write(char const* name, std::uint8_t* data, std::uint32_t dataLength); + void WriteAsync(char const* name, + std::uint8_t* data, + std::uint32_t dataLength, + std::function callback); + Result Delete(char const* name); + Result Exists(char const* name, bool* exists); + void Count(std::int32_t* count); + Result Stat(char const* name, FileStat* stat); + Result StatAt(std::int32_t index, FileStat* stat); + Result GetPath(char path[4096]); + +private: + friend class Core; + + StorageManager() = default; + StorageManager(StorageManager const& rhs) = delete; + StorageManager& operator=(StorageManager const& rhs) = delete; + StorageManager(StorageManager&& rhs) = delete; + StorageManager& operator=(StorageManager&& rhs) = delete; + + IDiscordStorageManager* internal_; + static IDiscordStorageEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/store_manager.cpp b/Crawler/discord-files/store_manager.cpp new file mode 100644 index 00000000..40c7e656 --- /dev/null +++ b/Crawler/discord-files/store_manager.cpp @@ -0,0 +1,162 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "store_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class StoreEvents final { +public: + static void DISCORD_CALLBACK OnEntitlementCreate(void* callbackData, + DiscordEntitlement* entitlement) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->StoreManager(); + module.OnEntitlementCreate(*reinterpret_cast(entitlement)); + } + + static void DISCORD_CALLBACK OnEntitlementDelete(void* callbackData, + DiscordEntitlement* entitlement) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->StoreManager(); + module.OnEntitlementDelete(*reinterpret_cast(entitlement)); + } +}; + +IDiscordStoreEvents StoreManager::events_{ + &StoreEvents::OnEntitlementCreate, + &StoreEvents::OnEntitlementDelete, +}; + +void StoreManager::FetchSkus(std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->fetch_skus(internal_, cb.release(), wrapper); +} + +void StoreManager::CountSkus(std::int32_t* count) +{ + if (!count) { + return; + } + + internal_->count_skus(internal_, reinterpret_cast(count)); +} + +Result StoreManager::GetSku(Snowflake skuId, Sku* sku) +{ + if (!sku) { + return Result::InternalError; + } + + auto result = internal_->get_sku(internal_, skuId, reinterpret_cast(sku)); + return static_cast(result); +} + +Result StoreManager::GetSkuAt(std::int32_t index, Sku* sku) +{ + if (!sku) { + return Result::InternalError; + } + + auto result = internal_->get_sku_at(internal_, index, reinterpret_cast(sku)); + return static_cast(result); +} + +void StoreManager::FetchEntitlements(std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->fetch_entitlements(internal_, cb.release(), wrapper); +} + +void StoreManager::CountEntitlements(std::int32_t* count) +{ + if (!count) { + return; + } + + internal_->count_entitlements(internal_, reinterpret_cast(count)); +} + +Result StoreManager::GetEntitlement(Snowflake entitlementId, Entitlement* entitlement) +{ + if (!entitlement) { + return Result::InternalError; + } + + auto result = internal_->get_entitlement( + internal_, entitlementId, reinterpret_cast(entitlement)); + return static_cast(result); +} + +Result StoreManager::GetEntitlementAt(std::int32_t index, Entitlement* entitlement) +{ + if (!entitlement) { + return Result::InternalError; + } + + auto result = internal_->get_entitlement_at( + internal_, index, reinterpret_cast(entitlement)); + return static_cast(result); +} + +Result StoreManager::HasSkuEntitlement(Snowflake skuId, bool* hasEntitlement) +{ + if (!hasEntitlement) { + return Result::InternalError; + } + + auto result = + internal_->has_sku_entitlement(internal_, skuId, reinterpret_cast(hasEntitlement)); + return static_cast(result); +} + +void StoreManager::StartPurchase(Snowflake skuId, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->start_purchase(internal_, skuId, cb.release(), wrapper); +} + +} // namespace discord diff --git a/Crawler/discord-files/store_manager.h b/Crawler/discord-files/store_manager.h new file mode 100644 index 00000000..8e0df8ac --- /dev/null +++ b/Crawler/discord-files/store_manager.h @@ -0,0 +1,38 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class StoreManager final { +public: + ~StoreManager() = default; + + void FetchSkus(std::function callback); + void CountSkus(std::int32_t* count); + Result GetSku(Snowflake skuId, Sku* sku); + Result GetSkuAt(std::int32_t index, Sku* sku); + void FetchEntitlements(std::function callback); + void CountEntitlements(std::int32_t* count); + Result GetEntitlement(Snowflake entitlementId, Entitlement* entitlement); + Result GetEntitlementAt(std::int32_t index, Entitlement* entitlement); + Result HasSkuEntitlement(Snowflake skuId, bool* hasEntitlement); + void StartPurchase(Snowflake skuId, std::function callback); + + Event OnEntitlementCreate; + Event OnEntitlementDelete; + +private: + friend class Core; + + StoreManager() = default; + StoreManager(StoreManager const& rhs) = delete; + StoreManager& operator=(StoreManager const& rhs) = delete; + StoreManager(StoreManager&& rhs) = delete; + StoreManager& operator=(StoreManager&& rhs) = delete; + + IDiscordStoreManager* internal_; + static IDiscordStoreEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/types.cpp b/Crawler/discord-files/types.cpp new file mode 100644 index 00000000..b60dded0 --- /dev/null +++ b/Crawler/discord-files/types.cpp @@ -0,0 +1,879 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "types.h" + +#include +#include + +namespace discord { + +void User::SetId(UserId id) +{ + internal_.id = id; +} + +UserId User::GetId() const +{ + return internal_.id; +} + +void User::SetUsername(char const* username) +{ + strncpy(internal_.username, username, 256); + internal_.username[256 - 1] = '\0'; +} + +char const* User::GetUsername() const +{ + return internal_.username; +} + +void User::SetDiscriminator(char const* discriminator) +{ + strncpy(internal_.discriminator, discriminator, 8); + internal_.discriminator[8 - 1] = '\0'; +} + +char const* User::GetDiscriminator() const +{ + return internal_.discriminator; +} + +void User::SetAvatar(char const* avatar) +{ + strncpy(internal_.avatar, avatar, 128); + internal_.avatar[128 - 1] = '\0'; +} + +char const* User::GetAvatar() const +{ + return internal_.avatar; +} + +void User::SetBot(bool bot) +{ + internal_.bot = bot; +} + +bool User::GetBot() const +{ + return internal_.bot != 0; +} + +void OAuth2Token::SetAccessToken(char const* accessToken) +{ + strncpy(internal_.access_token, accessToken, 128); + internal_.access_token[128 - 1] = '\0'; +} + +char const* OAuth2Token::GetAccessToken() const +{ + return internal_.access_token; +} + +void OAuth2Token::SetScopes(char const* scopes) +{ + strncpy(internal_.scopes, scopes, 1024); + internal_.scopes[1024 - 1] = '\0'; +} + +char const* OAuth2Token::GetScopes() const +{ + return internal_.scopes; +} + +void OAuth2Token::SetExpires(Timestamp expires) +{ + internal_.expires = expires; +} + +Timestamp OAuth2Token::GetExpires() const +{ + return internal_.expires; +} + +void ImageHandle::SetType(ImageType type) +{ + internal_.type = static_cast(type); +} + +ImageType ImageHandle::GetType() const +{ + return static_cast(internal_.type); +} + +void ImageHandle::SetId(std::int64_t id) +{ + internal_.id = id; +} + +std::int64_t ImageHandle::GetId() const +{ + return internal_.id; +} + +void ImageHandle::SetSize(std::uint32_t size) +{ + internal_.size = size; +} + +std::uint32_t ImageHandle::GetSize() const +{ + return internal_.size; +} + +void ImageDimensions::SetWidth(std::uint32_t width) +{ + internal_.width = width; +} + +std::uint32_t ImageDimensions::GetWidth() const +{ + return internal_.width; +} + +void ImageDimensions::SetHeight(std::uint32_t height) +{ + internal_.height = height; +} + +std::uint32_t ImageDimensions::GetHeight() const +{ + return internal_.height; +} + +void ActivityTimestamps::SetStart(Timestamp start) +{ + internal_.start = start; +} + +Timestamp ActivityTimestamps::GetStart() const +{ + return internal_.start; +} + +void ActivityTimestamps::SetEnd(Timestamp end) +{ + internal_.end = end; +} + +Timestamp ActivityTimestamps::GetEnd() const +{ + return internal_.end; +} + +void ActivityAssets::SetLargeImage(char const* largeImage) +{ + strncpy(internal_.large_image, largeImage, 128); + internal_.large_image[128 - 1] = '\0'; +} + +char const* ActivityAssets::GetLargeImage() const +{ + return internal_.large_image; +} + +void ActivityAssets::SetLargeText(char const* largeText) +{ + strncpy(internal_.large_text, largeText, 128); + internal_.large_text[128 - 1] = '\0'; +} + +char const* ActivityAssets::GetLargeText() const +{ + return internal_.large_text; +} + +void ActivityAssets::SetSmallImage(char const* smallImage) +{ + strncpy(internal_.small_image, smallImage, 128); + internal_.small_image[128 - 1] = '\0'; +} + +char const* ActivityAssets::GetSmallImage() const +{ + return internal_.small_image; +} + +void ActivityAssets::SetSmallText(char const* smallText) +{ + strncpy(internal_.small_text, smallText, 128); + internal_.small_text[128 - 1] = '\0'; +} + +char const* ActivityAssets::GetSmallText() const +{ + return internal_.small_text; +} + +void PartySize::SetCurrentSize(std::int32_t currentSize) +{ + internal_.current_size = currentSize; +} + +std::int32_t PartySize::GetCurrentSize() const +{ + return internal_.current_size; +} + +void PartySize::SetMaxSize(std::int32_t maxSize) +{ + internal_.max_size = maxSize; +} + +std::int32_t PartySize::GetMaxSize() const +{ + return internal_.max_size; +} + +void ActivityParty::SetId(char const* id) +{ + strncpy(internal_.id, id, 128); + internal_.id[128 - 1] = '\0'; +} + +char const* ActivityParty::GetId() const +{ + return internal_.id; +} + +PartySize& ActivityParty::GetSize() +{ + return reinterpret_cast(internal_.size); +} + +PartySize const& ActivityParty::GetSize() const +{ + return reinterpret_cast(internal_.size); +} + +void ActivityParty::SetPrivacy(ActivityPartyPrivacy privacy) +{ + internal_.privacy = static_cast(privacy); +} + +ActivityPartyPrivacy ActivityParty::GetPrivacy() const +{ + return static_cast(internal_.privacy); +} + +void ActivitySecrets::SetMatch(char const* match) +{ + strncpy(internal_.match, match, 128); + internal_.match[128 - 1] = '\0'; +} + +char const* ActivitySecrets::GetMatch() const +{ + return internal_.match; +} + +void ActivitySecrets::SetJoin(char const* join) +{ + strncpy(internal_.join, join, 128); + internal_.join[128 - 1] = '\0'; +} + +char const* ActivitySecrets::GetJoin() const +{ + return internal_.join; +} + +void ActivitySecrets::SetSpectate(char const* spectate) +{ + strncpy(internal_.spectate, spectate, 128); + internal_.spectate[128 - 1] = '\0'; +} + +char const* ActivitySecrets::GetSpectate() const +{ + return internal_.spectate; +} + +void Activity::SetType(ActivityType type) +{ + internal_.type = static_cast(type); +} + +ActivityType Activity::GetType() const +{ + return static_cast(internal_.type); +} + +void Activity::SetApplicationId(std::int64_t applicationId) +{ + internal_.application_id = applicationId; +} + +std::int64_t Activity::GetApplicationId() const +{ + return internal_.application_id; +} + +void Activity::SetName(char const* name) +{ + strncpy(internal_.name, name, 128); + internal_.name[128 - 1] = '\0'; +} + +char const* Activity::GetName() const +{ + return internal_.name; +} + +void Activity::SetState(char const* state) +{ + strncpy(internal_.state, state, 128); + internal_.state[128 - 1] = '\0'; +} + +char const* Activity::GetState() const +{ + return internal_.state; +} + +void Activity::SetDetails(char const* details) +{ + strncpy(internal_.details, details, 128); + internal_.details[128 - 1] = '\0'; +} + +char const* Activity::GetDetails() const +{ + return internal_.details; +} + +ActivityTimestamps& Activity::GetTimestamps() +{ + return reinterpret_cast(internal_.timestamps); +} + +ActivityTimestamps const& Activity::GetTimestamps() const +{ + return reinterpret_cast(internal_.timestamps); +} + +ActivityAssets& Activity::GetAssets() +{ + return reinterpret_cast(internal_.assets); +} + +ActivityAssets const& Activity::GetAssets() const +{ + return reinterpret_cast(internal_.assets); +} + +ActivityParty& Activity::GetParty() +{ + return reinterpret_cast(internal_.party); +} + +ActivityParty const& Activity::GetParty() const +{ + return reinterpret_cast(internal_.party); +} + +ActivitySecrets& Activity::GetSecrets() +{ + return reinterpret_cast(internal_.secrets); +} + +ActivitySecrets const& Activity::GetSecrets() const +{ + return reinterpret_cast(internal_.secrets); +} + +void Activity::SetInstance(bool instance) +{ + internal_.instance = instance; +} + +bool Activity::GetInstance() const +{ + return internal_.instance != 0; +} + +void Activity::SetSupportedPlatforms(std::uint32_t supportedPlatforms) +{ + internal_.supported_platforms = supportedPlatforms; +} + +std::uint32_t Activity::GetSupportedPlatforms() const +{ + return internal_.supported_platforms; +} + +void Presence::SetStatus(Status status) +{ + internal_.status = static_cast(status); +} + +Status Presence::GetStatus() const +{ + return static_cast(internal_.status); +} + +Activity& Presence::GetActivity() +{ + return reinterpret_cast(internal_.activity); +} + +Activity const& Presence::GetActivity() const +{ + return reinterpret_cast(internal_.activity); +} + +void Relationship::SetType(RelationshipType type) +{ + internal_.type = static_cast(type); +} + +RelationshipType Relationship::GetType() const +{ + return static_cast(internal_.type); +} + +User& Relationship::GetUser() +{ + return reinterpret_cast(internal_.user); +} + +User const& Relationship::GetUser() const +{ + return reinterpret_cast(internal_.user); +} + +Presence& Relationship::GetPresence() +{ + return reinterpret_cast(internal_.presence); +} + +Presence const& Relationship::GetPresence() const +{ + return reinterpret_cast(internal_.presence); +} + +void Lobby::SetId(LobbyId id) +{ + internal_.id = id; +} + +LobbyId Lobby::GetId() const +{ + return internal_.id; +} + +void Lobby::SetType(LobbyType type) +{ + internal_.type = static_cast(type); +} + +LobbyType Lobby::GetType() const +{ + return static_cast(internal_.type); +} + +void Lobby::SetOwnerId(UserId ownerId) +{ + internal_.owner_id = ownerId; +} + +UserId Lobby::GetOwnerId() const +{ + return internal_.owner_id; +} + +void Lobby::SetSecret(LobbySecret secret) +{ + strncpy(internal_.secret, secret, 128); + internal_.secret[128 - 1] = '\0'; +} + +LobbySecret Lobby::GetSecret() const +{ + return internal_.secret; +} + +void Lobby::SetCapacity(std::uint32_t capacity) +{ + internal_.capacity = capacity; +} + +std::uint32_t Lobby::GetCapacity() const +{ + return internal_.capacity; +} + +void Lobby::SetLocked(bool locked) +{ + internal_.locked = locked; +} + +bool Lobby::GetLocked() const +{ + return internal_.locked != 0; +} + +void ImeUnderline::SetFrom(std::int32_t from) +{ + internal_.from = from; +} + +std::int32_t ImeUnderline::GetFrom() const +{ + return internal_.from; +} + +void ImeUnderline::SetTo(std::int32_t to) +{ + internal_.to = to; +} + +std::int32_t ImeUnderline::GetTo() const +{ + return internal_.to; +} + +void ImeUnderline::SetColor(std::uint32_t color) +{ + internal_.color = color; +} + +std::uint32_t ImeUnderline::GetColor() const +{ + return internal_.color; +} + +void ImeUnderline::SetBackgroundColor(std::uint32_t backgroundColor) +{ + internal_.background_color = backgroundColor; +} + +std::uint32_t ImeUnderline::GetBackgroundColor() const +{ + return internal_.background_color; +} + +void ImeUnderline::SetThick(bool thick) +{ + internal_.thick = thick; +} + +bool ImeUnderline::GetThick() const +{ + return internal_.thick != 0; +} + +void Rect::SetLeft(std::int32_t left) +{ + internal_.left = left; +} + +std::int32_t Rect::GetLeft() const +{ + return internal_.left; +} + +void Rect::SetTop(std::int32_t top) +{ + internal_.top = top; +} + +std::int32_t Rect::GetTop() const +{ + return internal_.top; +} + +void Rect::SetRight(std::int32_t right) +{ + internal_.right = right; +} + +std::int32_t Rect::GetRight() const +{ + return internal_.right; +} + +void Rect::SetBottom(std::int32_t bottom) +{ + internal_.bottom = bottom; +} + +std::int32_t Rect::GetBottom() const +{ + return internal_.bottom; +} + +void FileStat::SetFilename(char const* filename) +{ + strncpy(internal_.filename, filename, 260); + internal_.filename[260 - 1] = '\0'; +} + +char const* FileStat::GetFilename() const +{ + return internal_.filename; +} + +void FileStat::SetSize(std::uint64_t size) +{ + internal_.size = size; +} + +std::uint64_t FileStat::GetSize() const +{ + return internal_.size; +} + +void FileStat::SetLastModified(std::uint64_t lastModified) +{ + internal_.last_modified = lastModified; +} + +std::uint64_t FileStat::GetLastModified() const +{ + return internal_.last_modified; +} + +void Entitlement::SetId(Snowflake id) +{ + internal_.id = id; +} + +Snowflake Entitlement::GetId() const +{ + return internal_.id; +} + +void Entitlement::SetType(EntitlementType type) +{ + internal_.type = static_cast(type); +} + +EntitlementType Entitlement::GetType() const +{ + return static_cast(internal_.type); +} + +void Entitlement::SetSkuId(Snowflake skuId) +{ + internal_.sku_id = skuId; +} + +Snowflake Entitlement::GetSkuId() const +{ + return internal_.sku_id; +} + +void SkuPrice::SetAmount(std::uint32_t amount) +{ + internal_.amount = amount; +} + +std::uint32_t SkuPrice::GetAmount() const +{ + return internal_.amount; +} + +void SkuPrice::SetCurrency(char const* currency) +{ + strncpy(internal_.currency, currency, 16); + internal_.currency[16 - 1] = '\0'; +} + +char const* SkuPrice::GetCurrency() const +{ + return internal_.currency; +} + +void Sku::SetId(Snowflake id) +{ + internal_.id = id; +} + +Snowflake Sku::GetId() const +{ + return internal_.id; +} + +void Sku::SetType(SkuType type) +{ + internal_.type = static_cast(type); +} + +SkuType Sku::GetType() const +{ + return static_cast(internal_.type); +} + +void Sku::SetName(char const* name) +{ + strncpy(internal_.name, name, 256); + internal_.name[256 - 1] = '\0'; +} + +char const* Sku::GetName() const +{ + return internal_.name; +} + +SkuPrice& Sku::GetPrice() +{ + return reinterpret_cast(internal_.price); +} + +SkuPrice const& Sku::GetPrice() const +{ + return reinterpret_cast(internal_.price); +} + +void InputMode::SetType(InputModeType type) +{ + internal_.type = static_cast(type); +} + +InputModeType InputMode::GetType() const +{ + return static_cast(internal_.type); +} + +void InputMode::SetShortcut(char const* shortcut) +{ + strncpy(internal_.shortcut, shortcut, 256); + internal_.shortcut[256 - 1] = '\0'; +} + +char const* InputMode::GetShortcut() const +{ + return internal_.shortcut; +} + +void UserAchievement::SetUserId(Snowflake userId) +{ + internal_.user_id = userId; +} + +Snowflake UserAchievement::GetUserId() const +{ + return internal_.user_id; +} + +void UserAchievement::SetAchievementId(Snowflake achievementId) +{ + internal_.achievement_id = achievementId; +} + +Snowflake UserAchievement::GetAchievementId() const +{ + return internal_.achievement_id; +} + +void UserAchievement::SetPercentComplete(std::uint8_t percentComplete) +{ + internal_.percent_complete = percentComplete; +} + +std::uint8_t UserAchievement::GetPercentComplete() const +{ + return internal_.percent_complete; +} + +void UserAchievement::SetUnlockedAt(DateTime unlockedAt) +{ + strncpy(internal_.unlocked_at, unlockedAt, 64); + internal_.unlocked_at[64 - 1] = '\0'; +} + +DateTime UserAchievement::GetUnlockedAt() const +{ + return internal_.unlocked_at; +} + +Result LobbyTransaction::SetType(LobbyType type) +{ + auto result = internal_->set_type(internal_, static_cast(type)); + return static_cast(result); +} + +Result LobbyTransaction::SetOwner(UserId ownerId) +{ + auto result = internal_->set_owner(internal_, ownerId); + return static_cast(result); +} + +Result LobbyTransaction::SetCapacity(std::uint32_t capacity) +{ + auto result = internal_->set_capacity(internal_, capacity); + return static_cast(result); +} + +Result LobbyTransaction::SetMetadata(MetadataKey key, MetadataValue value) +{ + auto result = + internal_->set_metadata(internal_, const_cast(key), const_cast(value)); + return static_cast(result); +} + +Result LobbyTransaction::DeleteMetadata(MetadataKey key) +{ + auto result = internal_->delete_metadata(internal_, const_cast(key)); + return static_cast(result); +} + +Result LobbyTransaction::SetLocked(bool locked) +{ + auto result = internal_->set_locked(internal_, (locked ? 1 : 0)); + return static_cast(result); +} + +Result LobbyMemberTransaction::SetMetadata(MetadataKey key, MetadataValue value) +{ + auto result = + internal_->set_metadata(internal_, const_cast(key), const_cast(value)); + return static_cast(result); +} + +Result LobbyMemberTransaction::DeleteMetadata(MetadataKey key) +{ + auto result = internal_->delete_metadata(internal_, const_cast(key)); + return static_cast(result); +} + +Result LobbySearchQuery::Filter(MetadataKey key, + LobbySearchComparison comparison, + LobbySearchCast cast, + MetadataValue value) +{ + auto result = internal_->filter(internal_, + const_cast(key), + static_cast(comparison), + static_cast(cast), + const_cast(value)); + return static_cast(result); +} + +Result LobbySearchQuery::Sort(MetadataKey key, LobbySearchCast cast, MetadataValue value) +{ + auto result = internal_->sort(internal_, + const_cast(key), + static_cast(cast), + const_cast(value)); + return static_cast(result); +} + +Result LobbySearchQuery::Limit(std::uint32_t limit) +{ + auto result = internal_->limit(internal_, limit); + return static_cast(result); +} + +Result LobbySearchQuery::Distance(LobbySearchDistance distance) +{ + auto result = + internal_->distance(internal_, static_cast(distance)); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/types.h b/Crawler/discord-files/types.h new file mode 100644 index 00000000..76c43117 --- /dev/null +++ b/Crawler/discord-files/types.h @@ -0,0 +1,567 @@ +#pragma once + +#include "ffi.h" +#include "event.h" +#ifdef _WIN32 +#include +#include +#endif + +namespace discord { + +enum class Result { + Ok = 0, + ServiceUnavailable = 1, + InvalidVersion = 2, + LockFailed = 3, + InternalError = 4, + InvalidPayload = 5, + InvalidCommand = 6, + InvalidPermissions = 7, + NotFetched = 8, + NotFound = 9, + Conflict = 10, + InvalidSecret = 11, + InvalidJoinSecret = 12, + NoEligibleActivity = 13, + InvalidInvite = 14, + NotAuthenticated = 15, + InvalidAccessToken = 16, + ApplicationMismatch = 17, + InvalidDataUrl = 18, + InvalidBase64 = 19, + NotFiltered = 20, + LobbyFull = 21, + InvalidLobbySecret = 22, + InvalidFilename = 23, + InvalidFileSize = 24, + InvalidEntitlement = 25, + NotInstalled = 26, + NotRunning = 27, + InsufficientBuffer = 28, + PurchaseCanceled = 29, + InvalidGuild = 30, + InvalidEvent = 31, + InvalidChannel = 32, + InvalidOrigin = 33, + RateLimited = 34, + OAuth2Error = 35, + SelectChannelTimeout = 36, + GetGuildTimeout = 37, + SelectVoiceForceRequired = 38, + CaptureShortcutAlreadyListening = 39, + UnauthorizedForAchievement = 40, + InvalidGiftCode = 41, + PurchaseError = 42, + TransactionAborted = 43, + DrawingInitFailed = 44, +}; + +enum class CreateFlags { + Default = 0, + NoRequireDiscord = 1, +}; + +enum class LogLevel { + Error = 1, + Warn, + Info, + Debug, +}; + +enum class UserFlag { + Partner = 2, + HypeSquadEvents = 4, + HypeSquadHouse1 = 64, + HypeSquadHouse2 = 128, + HypeSquadHouse3 = 256, +}; + +enum class PremiumType { + None = 0, + Tier1 = 1, + Tier2 = 2, +}; + +enum class ImageType { + User, +}; + +enum class ActivityPartyPrivacy { + Private = 0, + Public = 1, +}; + +enum class ActivityType { + Playing, + Streaming, + Listening, + Watching, +}; + +enum class ActivityActionType { + Join = 1, + Spectate, +}; + +enum class ActivitySupportedPlatformFlags { + Desktop = 1, + Android = 2, + iOS = 4, +}; + +enum class ActivityJoinRequestReply { + No, + Yes, + Ignore, +}; + +enum class Status { + Offline = 0, + Online = 1, + Idle = 2, + DoNotDisturb = 3, +}; + +enum class RelationshipType { + None, + Friend, + Blocked, + PendingIncoming, + PendingOutgoing, + Implicit, +}; + +enum class LobbyType { + Private = 1, + Public, +}; + +enum class LobbySearchComparison { + LessThanOrEqual = -2, + LessThan, + Equal, + GreaterThan, + GreaterThanOrEqual, + NotEqual, +}; + +enum class LobbySearchCast { + String = 1, + Number, +}; + +enum class LobbySearchDistance { + Local, + Default, + Extended, + Global, +}; + +enum class KeyVariant { + Normal, + Right, + Left, +}; + +enum class MouseButton { + Left, + Middle, + Right, +}; + +enum class EntitlementType { + Purchase = 1, + PremiumSubscription, + DeveloperGift, + TestModePurchase, + FreePurchase, + UserGift, + PremiumPurchase, +}; + +enum class SkuType { + Application = 1, + DLC, + Consumable, + Bundle, +}; + +enum class InputModeType { + VoiceActivity = 0, + PushToTalk, +}; + +using ClientId = std::int64_t; +using Version = std::int32_t; +using Snowflake = std::int64_t; +using Timestamp = std::int64_t; +using UserId = Snowflake; +using Locale = char const*; +using Branch = char const*; +using LobbyId = Snowflake; +using LobbySecret = char const*; +using MetadataKey = char const*; +using MetadataValue = char const*; +using NetworkPeerId = std::uint64_t; +using NetworkChannelId = std::uint8_t; +#ifdef __APPLE__ +using IDXGISwapChain = void; +#endif +#ifdef __linux__ +using IDXGISwapChain = void; +#endif +#ifdef __APPLE__ +using MSG = void; +#endif +#ifdef __linux__ +using MSG = void; +#endif +using Path = char const*; +using DateTime = char const*; + +class User final { +public: + void SetId(UserId id); + UserId GetId() const; + void SetUsername(char const* username); + char const* GetUsername() const; + void SetDiscriminator(char const* discriminator); + char const* GetDiscriminator() const; + void SetAvatar(char const* avatar); + char const* GetAvatar() const; + void SetBot(bool bot); + bool GetBot() const; + +private: + DiscordUser internal_; +}; + +class OAuth2Token final { +public: + void SetAccessToken(char const* accessToken); + char const* GetAccessToken() const; + void SetScopes(char const* scopes); + char const* GetScopes() const; + void SetExpires(Timestamp expires); + Timestamp GetExpires() const; + +private: + DiscordOAuth2Token internal_; +}; + +class ImageHandle final { +public: + void SetType(ImageType type); + ImageType GetType() const; + void SetId(std::int64_t id); + std::int64_t GetId() const; + void SetSize(std::uint32_t size); + std::uint32_t GetSize() const; + +private: + DiscordImageHandle internal_; +}; + +class ImageDimensions final { +public: + void SetWidth(std::uint32_t width); + std::uint32_t GetWidth() const; + void SetHeight(std::uint32_t height); + std::uint32_t GetHeight() const; + +private: + DiscordImageDimensions internal_; +}; + +class ActivityTimestamps final { +public: + void SetStart(Timestamp start); + Timestamp GetStart() const; + void SetEnd(Timestamp end); + Timestamp GetEnd() const; + +private: + DiscordActivityTimestamps internal_; +}; + +class ActivityAssets final { +public: + void SetLargeImage(char const* largeImage); + char const* GetLargeImage() const; + void SetLargeText(char const* largeText); + char const* GetLargeText() const; + void SetSmallImage(char const* smallImage); + char const* GetSmallImage() const; + void SetSmallText(char const* smallText); + char const* GetSmallText() const; + +private: + DiscordActivityAssets internal_; +}; + +class PartySize final { +public: + void SetCurrentSize(std::int32_t currentSize); + std::int32_t GetCurrentSize() const; + void SetMaxSize(std::int32_t maxSize); + std::int32_t GetMaxSize() const; + +private: + DiscordPartySize internal_; +}; + +class ActivityParty final { +public: + void SetId(char const* id); + char const* GetId() const; + PartySize& GetSize(); + PartySize const& GetSize() const; + void SetPrivacy(ActivityPartyPrivacy privacy); + ActivityPartyPrivacy GetPrivacy() const; + +private: + DiscordActivityParty internal_; +}; + +class ActivitySecrets final { +public: + void SetMatch(char const* match); + char const* GetMatch() const; + void SetJoin(char const* join); + char const* GetJoin() const; + void SetSpectate(char const* spectate); + char const* GetSpectate() const; + +private: + DiscordActivitySecrets internal_; +}; + +class Activity final { +public: + void SetType(ActivityType type); + ActivityType GetType() const; + void SetApplicationId(std::int64_t applicationId); + std::int64_t GetApplicationId() const; + void SetName(char const* name); + char const* GetName() const; + void SetState(char const* state); + char const* GetState() const; + void SetDetails(char const* details); + char const* GetDetails() const; + ActivityTimestamps& GetTimestamps(); + ActivityTimestamps const& GetTimestamps() const; + ActivityAssets& GetAssets(); + ActivityAssets const& GetAssets() const; + ActivityParty& GetParty(); + ActivityParty const& GetParty() const; + ActivitySecrets& GetSecrets(); + ActivitySecrets const& GetSecrets() const; + void SetInstance(bool instance); + bool GetInstance() const; + void SetSupportedPlatforms(std::uint32_t supportedPlatforms); + std::uint32_t GetSupportedPlatforms() const; + +private: + DiscordActivity internal_; +}; + +class Presence final { +public: + void SetStatus(Status status); + Status GetStatus() const; + Activity& GetActivity(); + Activity const& GetActivity() const; + +private: + DiscordPresence internal_; +}; + +class Relationship final { +public: + void SetType(RelationshipType type); + RelationshipType GetType() const; + User& GetUser(); + User const& GetUser() const; + Presence& GetPresence(); + Presence const& GetPresence() const; + +private: + DiscordRelationship internal_; +}; + +class Lobby final { +public: + void SetId(LobbyId id); + LobbyId GetId() const; + void SetType(LobbyType type); + LobbyType GetType() const; + void SetOwnerId(UserId ownerId); + UserId GetOwnerId() const; + void SetSecret(LobbySecret secret); + LobbySecret GetSecret() const; + void SetCapacity(std::uint32_t capacity); + std::uint32_t GetCapacity() const; + void SetLocked(bool locked); + bool GetLocked() const; + +private: + DiscordLobby internal_; +}; + +class ImeUnderline final { +public: + void SetFrom(std::int32_t from); + std::int32_t GetFrom() const; + void SetTo(std::int32_t to); + std::int32_t GetTo() const; + void SetColor(std::uint32_t color); + std::uint32_t GetColor() const; + void SetBackgroundColor(std::uint32_t backgroundColor); + std::uint32_t GetBackgroundColor() const; + void SetThick(bool thick); + bool GetThick() const; + +private: + DiscordImeUnderline internal_; +}; + +class Rect final { +public: + void SetLeft(std::int32_t left); + std::int32_t GetLeft() const; + void SetTop(std::int32_t top); + std::int32_t GetTop() const; + void SetRight(std::int32_t right); + std::int32_t GetRight() const; + void SetBottom(std::int32_t bottom); + std::int32_t GetBottom() const; + +private: + DiscordRect internal_; +}; + +class FileStat final { +public: + void SetFilename(char const* filename); + char const* GetFilename() const; + void SetSize(std::uint64_t size); + std::uint64_t GetSize() const; + void SetLastModified(std::uint64_t lastModified); + std::uint64_t GetLastModified() const; + +private: + DiscordFileStat internal_; +}; + +class Entitlement final { +public: + void SetId(Snowflake id); + Snowflake GetId() const; + void SetType(EntitlementType type); + EntitlementType GetType() const; + void SetSkuId(Snowflake skuId); + Snowflake GetSkuId() const; + +private: + DiscordEntitlement internal_; +}; + +class SkuPrice final { +public: + void SetAmount(std::uint32_t amount); + std::uint32_t GetAmount() const; + void SetCurrency(char const* currency); + char const* GetCurrency() const; + +private: + DiscordSkuPrice internal_; +}; + +class Sku final { +public: + void SetId(Snowflake id); + Snowflake GetId() const; + void SetType(SkuType type); + SkuType GetType() const; + void SetName(char const* name); + char const* GetName() const; + SkuPrice& GetPrice(); + SkuPrice const& GetPrice() const; + +private: + DiscordSku internal_; +}; + +class InputMode final { +public: + void SetType(InputModeType type); + InputModeType GetType() const; + void SetShortcut(char const* shortcut); + char const* GetShortcut() const; + +private: + DiscordInputMode internal_; +}; + +class UserAchievement final { +public: + void SetUserId(Snowflake userId); + Snowflake GetUserId() const; + void SetAchievementId(Snowflake achievementId); + Snowflake GetAchievementId() const; + void SetPercentComplete(std::uint8_t percentComplete); + std::uint8_t GetPercentComplete() const; + void SetUnlockedAt(DateTime unlockedAt); + DateTime GetUnlockedAt() const; + +private: + DiscordUserAchievement internal_; +}; + +class LobbyTransaction final { +public: + Result SetType(LobbyType type); + Result SetOwner(UserId ownerId); + Result SetCapacity(std::uint32_t capacity); + Result SetMetadata(MetadataKey key, MetadataValue value); + Result DeleteMetadata(MetadataKey key); + Result SetLocked(bool locked); + + IDiscordLobbyTransaction** Receive() { return &internal_; } + IDiscordLobbyTransaction* Internal() { return internal_; } + +private: + IDiscordLobbyTransaction* internal_; +}; + +class LobbyMemberTransaction final { +public: + Result SetMetadata(MetadataKey key, MetadataValue value); + Result DeleteMetadata(MetadataKey key); + + IDiscordLobbyMemberTransaction** Receive() { return &internal_; } + IDiscordLobbyMemberTransaction* Internal() { return internal_; } + +private: + IDiscordLobbyMemberTransaction* internal_; +}; + +class LobbySearchQuery final { +public: + Result Filter(MetadataKey key, + LobbySearchComparison comparison, + LobbySearchCast cast, + MetadataValue value); + Result Sort(MetadataKey key, LobbySearchCast cast, MetadataValue value); + Result Limit(std::uint32_t limit); + Result Distance(LobbySearchDistance distance); + + IDiscordLobbySearchQuery** Receive() { return &internal_; } + IDiscordLobbySearchQuery* Internal() { return internal_; } + +private: + IDiscordLobbySearchQuery* internal_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/user_manager.cpp b/Crawler/discord-files/user_manager.cpp new file mode 100644 index 00000000..ddb6d5ca --- /dev/null +++ b/Crawler/discord-files/user_manager.cpp @@ -0,0 +1,80 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "user_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class UserEvents final { +public: + static void DISCORD_CALLBACK OnCurrentUserUpdate(void* callbackData) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->UserManager(); + module.OnCurrentUserUpdate(); + } +}; + +IDiscordUserEvents UserManager::events_{ + &UserEvents::OnCurrentUserUpdate, +}; + +Result UserManager::GetCurrentUser(User* currentUser) +{ + if (!currentUser) { + return Result::InternalError; + } + + auto result = + internal_->get_current_user(internal_, reinterpret_cast(currentUser)); + return static_cast(result); +} + +void UserManager::GetUser(UserId userId, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result, DiscordUser* user) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result), *reinterpret_cast(user)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->get_user(internal_, userId, cb.release(), wrapper); +} + +Result UserManager::GetCurrentUserPremiumType(PremiumType* premiumType) +{ + if (!premiumType) { + return Result::InternalError; + } + + auto result = internal_->get_current_user_premium_type( + internal_, reinterpret_cast(premiumType)); + return static_cast(result); +} + +Result UserManager::CurrentUserHasFlag(UserFlag flag, bool* hasFlag) +{ + if (!hasFlag) { + return Result::InternalError; + } + + auto result = internal_->current_user_has_flag( + internal_, static_cast(flag), reinterpret_cast(hasFlag)); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/user_manager.h b/Crawler/discord-files/user_manager.h new file mode 100644 index 00000000..d85de1b5 --- /dev/null +++ b/Crawler/discord-files/user_manager.h @@ -0,0 +1,31 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class UserManager final { +public: + ~UserManager() = default; + + Result GetCurrentUser(User* currentUser); + void GetUser(UserId userId, std::function callback); + Result GetCurrentUserPremiumType(PremiumType* premiumType); + Result CurrentUserHasFlag(UserFlag flag, bool* hasFlag); + + Event<> OnCurrentUserUpdate; + +private: + friend class Core; + + UserManager() = default; + UserManager(UserManager const& rhs) = delete; + UserManager& operator=(UserManager const& rhs) = delete; + UserManager(UserManager&& rhs) = delete; + UserManager& operator=(UserManager&& rhs) = delete; + + IDiscordUserManager* internal_; + static IDiscordUserEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord-files/voice_manager.cpp b/Crawler/discord-files/voice_manager.cpp new file mode 100644 index 00000000..014ceb3f --- /dev/null +++ b/Crawler/discord-files/voice_manager.cpp @@ -0,0 +1,124 @@ +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "voice_manager.h" + +#include "core.h" + +#include +#include + +namespace discord { + +class VoiceEvents final { +public: + static void DISCORD_CALLBACK OnSettingsUpdate(void* callbackData) + { + auto* core = reinterpret_cast(callbackData); + if (!core) { + return; + } + + auto& module = core->VoiceManager(); + module.OnSettingsUpdate(); + } +}; + +IDiscordVoiceEvents VoiceManager::events_{ + &VoiceEvents::OnSettingsUpdate, +}; + +Result VoiceManager::GetInputMode(InputMode* inputMode) +{ + if (!inputMode) { + return Result::InternalError; + } + + auto result = + internal_->get_input_mode(internal_, reinterpret_cast(inputMode)); + return static_cast(result); +} + +void VoiceManager::SetInputMode(InputMode inputMode, std::function callback) +{ + static auto wrapper = [](void* callbackData, EDiscordResult result) -> void { + std::unique_ptr> cb( + reinterpret_cast*>(callbackData)); + if (!cb || !(*cb)) { + return; + } + (*cb)(static_cast(result)); + }; + std::unique_ptr> cb{}; + cb.reset(new std::function(std::move(callback))); + internal_->set_input_mode( + internal_, *reinterpret_cast(&inputMode), cb.release(), wrapper); +} + +Result VoiceManager::IsSelfMute(bool* mute) +{ + if (!mute) { + return Result::InternalError; + } + + auto result = internal_->is_self_mute(internal_, reinterpret_cast(mute)); + return static_cast(result); +} + +Result VoiceManager::SetSelfMute(bool mute) +{ + auto result = internal_->set_self_mute(internal_, (mute ? 1 : 0)); + return static_cast(result); +} + +Result VoiceManager::IsSelfDeaf(bool* deaf) +{ + if (!deaf) { + return Result::InternalError; + } + + auto result = internal_->is_self_deaf(internal_, reinterpret_cast(deaf)); + return static_cast(result); +} + +Result VoiceManager::SetSelfDeaf(bool deaf) +{ + auto result = internal_->set_self_deaf(internal_, (deaf ? 1 : 0)); + return static_cast(result); +} + +Result VoiceManager::IsLocalMute(Snowflake userId, bool* mute) +{ + if (!mute) { + return Result::InternalError; + } + + auto result = internal_->is_local_mute(internal_, userId, reinterpret_cast(mute)); + return static_cast(result); +} + +Result VoiceManager::SetLocalMute(Snowflake userId, bool mute) +{ + auto result = internal_->set_local_mute(internal_, userId, (mute ? 1 : 0)); + return static_cast(result); +} + +Result VoiceManager::GetLocalVolume(Snowflake userId, std::uint8_t* volume) +{ + if (!volume) { + return Result::InternalError; + } + + auto result = + internal_->get_local_volume(internal_, userId, reinterpret_cast(volume)); + return static_cast(result); +} + +Result VoiceManager::SetLocalVolume(Snowflake userId, std::uint8_t volume) +{ + auto result = internal_->set_local_volume(internal_, userId, volume); + return static_cast(result); +} + +} // namespace discord diff --git a/Crawler/discord-files/voice_manager.h b/Crawler/discord-files/voice_manager.h new file mode 100644 index 00000000..95b20e99 --- /dev/null +++ b/Crawler/discord-files/voice_manager.h @@ -0,0 +1,37 @@ +#pragma once + +#include "types.h" + +namespace discord { + +class VoiceManager final { +public: + ~VoiceManager() = default; + + Result GetInputMode(InputMode* inputMode); + void SetInputMode(InputMode inputMode, std::function callback); + Result IsSelfMute(bool* mute); + Result SetSelfMute(bool mute); + Result IsSelfDeaf(bool* deaf); + Result SetSelfDeaf(bool deaf); + Result IsLocalMute(Snowflake userId, bool* mute); + Result SetLocalMute(Snowflake userId, bool mute); + Result GetLocalVolume(Snowflake userId, std::uint8_t* volume); + Result SetLocalVolume(Snowflake userId, std::uint8_t volume); + + Event<> OnSettingsUpdate; + +private: + friend class Core; + + VoiceManager() = default; + VoiceManager(VoiceManager const& rhs) = delete; + VoiceManager& operator=(VoiceManager const& rhs) = delete; + VoiceManager(VoiceManager&& rhs) = delete; + VoiceManager& operator=(VoiceManager&& rhs) = delete; + + IDiscordVoiceManager* internal_; + static IDiscordVoiceEvents events_; +}; + +} // namespace discord diff --git a/Crawler/discord_game_sdk.bundle b/Crawler/discord_game_sdk.bundle new file mode 100644 index 0000000000000000000000000000000000000000..3402426b8de37b08289b1fa54f1324631357c458 GIT binary patch literal 4251528 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