From 8606e895372f859619e0521f1c1d37d5a1f9bc42 Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Tue, 23 Mar 2021 14:56:12 +0100 Subject: [PATCH] Initial project --- .gitignore | 79 ++++++ 4.26/DemoProject/Config/DefaultEditor.ini | 0 4.26/DemoProject/Config/DefaultEngine.ini | 36 +++ 4.26/DemoProject/Config/DefaultGame.ini | 7 + .../Config/DefaultLootLockerSDK.ini | 7 + .../Config/DefaultLootLockerServerSDK.ini | 6 + .../ServerDemo/LootLockerServerDemo.umap | Bin 0 -> 26331 bytes .../Content/ServerDemo/WBP_DemoAssets.uasset | Bin 0 -> 59881 bytes .../Content/ServerDemo/WBP_DemoAuth.uasset | Bin 0 -> 84494 bytes .../Content/ServerDemo/WBP_DemoCase.uasset | Bin 0 -> 46046 bytes .../ServerDemo/WBP_DemoCharacters.uasset | Bin 0 -> 262835 bytes .../Content/ServerDemo/WBP_DemoHeroes.uasset | Bin 0 -> 262188 bytes .../WBP_DemoLootLockerServer.uasset | Bin 0 -> 179366 bytes .../ServerDemo/WBP_DemoPlayerInventory.uasset | Bin 0 -> 265883 bytes .../WBP_DemoPlayerPersistentStorage.uasset | Bin 0 -> 162319 bytes .../ServerDemo/WBP_DemoTriggers.uasset | Bin 0 -> 81698 bytes 4.26/DemoProject/DemoProject.uproject | 22 ++ .../LootLockerServerSDK.uplugin | 24 ++ .../LootLockerServerSDK/Resources/Icon128.png | Bin 0 -> 4326 bytes .../LootLockerServerSDK.Build.cs | 53 ++++ .../Private/LootLockerServerConfig.cpp | 14 + .../Private/LootLockerServerGameEndpoints.cpp | 43 +++ .../Private/LootLockerServerHttpClient.cpp | 185 +++++++++++++ .../Private/LootLockerServerManager.cpp | 123 +++++++++ .../Private/LootLockerServerSDK.cpp | 42 +++ .../Private/LootLockerServerSDKManager.cpp | 129 +++++++++ .../LootLockerSrvPersitentDataHolder.cpp | 13 + .../LootLockerServerAssetRequest.cpp | 81 ++++++ .../ServerAPI/LootLockerServerAuthRequest.cpp | 79 ++++++ .../LootLockerServerCharacterRequest.cpp | 162 +++++++++++ .../LootLockerServerHeroesRequest.cpp | 161 +++++++++++ .../LootLockerServerPlayerRequest.cpp | 168 +++++++++++ .../LootLockerServerStorageRequest.cpp | 81 ++++++ .../LootLockerServerTriggerRequest.cpp | 47 ++++ .../Utils/LootLockerServerUtilities.cpp | 12 + .../Private/Utils/LootLockerServerUtilities.h | 8 + .../Public/LootLockerServerConfig.h | 69 +++++ .../Public/LootLockerServerGameEndpoints.h | 46 ++++ .../Public/LootLockerServerHttpClient.h | 26 ++ .../Public/LootLockerServerManager.h | 177 ++++++++++++ .../Public/LootLockerServerSDK.h | 15 + .../Public/LootLockerServerSDKManager.h | 211 ++++++++++++++ .../Public/LootLockerSrvPersitentDataHolder.h | 25 ++ .../ServerAPI/LootLockerServerAssetRequest.h | 260 ++++++++++++++++++ .../ServerAPI/LootLockerServerAuthRequest.h | 51 ++++ .../LootLockerServerCharacterRequest.h | 125 +++++++++ .../ServerAPI/LootLockerServerHeroesRequest.h | 137 +++++++++ .../ServerAPI/LootLockerServerPlayerRequest.h | 146 ++++++++++ .../LootLockerServerStorageRequest.h | 115 ++++++++ .../LootLockerServerTriggerRequest.h | 67 +++++ 4.26/DemoProject/Source/DemoProject.Target.cs | 14 + .../Source/DemoProject/DemoProject.Build.cs | 25 ++ .../Source/DemoProject/DemoProject.cpp | 6 + .../Source/DemoProject/DemoProject.h | 6 + .../DemoProject/DemoProjectGameModeBase.cpp | 5 + .../DemoProject/DemoProjectGameModeBase.h | 17 ++ .../Source/DemoProject/Private/DemoAssets.cpp | 23 ++ .../Source/DemoProject/Private/DemoAuth.cpp | 38 +++ .../DemoProject/Private/DemoCharacters.cpp | 91 ++++++ .../Source/DemoProject/Private/DemoHeroes.cpp | 92 +++++++ .../Private/DemoPlayerInventory.cpp | 97 +++++++ .../Private/DemoPlayerPersistentStorage.cpp | 55 ++++ .../DemoProject/Private/DemoTriggers.cpp | 25 ++ .../Source/DemoProject/Public/DemoAssets.h | 30 ++ .../Source/DemoProject/Public/DemoAuth.h | 25 ++ .../DemoProject/Public/DemoCharacters.h | 54 ++++ .../Source/DemoProject/Public/DemoHeroes.h | 54 ++++ .../DemoProject/Public/DemoPlayerInventory.h | 56 ++++ .../Public/DemoPlayerPersistentStorage.h | 38 +++ .../Source/DemoProject/Public/DemoTriggers.h | 27 ++ .../Source/DemoProjectEditor.Target.cs | 14 + .../LootLockerServerSDK.uplugin | 24 ++ .../LootLockerServerSDK/Resources/Icon128.png | Bin 0 -> 4326 bytes .../LootLockerServerSDK.Build.cs | 53 ++++ .../Private/LootLockerServerConfig.cpp | 14 + .../Private/LootLockerServerGameEndpoints.cpp | 43 +++ .../Private/LootLockerServerHttpClient.cpp | 185 +++++++++++++ .../Private/LootLockerServerManager.cpp | 123 +++++++++ .../Private/LootLockerServerSDK.cpp | 42 +++ .../Private/LootLockerServerSDKManager.cpp | 129 +++++++++ .../LootLockerSrvPersitentDataHolder.cpp | 13 + .../LootLockerServerAssetRequest.cpp | 81 ++++++ .../ServerAPI/LootLockerServerAuthRequest.cpp | 79 ++++++ .../LootLockerServerCharacterRequest.cpp | 162 +++++++++++ .../LootLockerServerHeroesRequest.cpp | 161 +++++++++++ .../LootLockerServerPlayerRequest.cpp | 168 +++++++++++ .../LootLockerServerStorageRequest.cpp | 81 ++++++ .../LootLockerServerTriggerRequest.cpp | 47 ++++ .../Utils/LootLockerServerUtilities.cpp | 12 + .../Private/Utils/LootLockerServerUtilities.h | 8 + .../Public/LootLockerServerConfig.h | 69 +++++ .../Public/LootLockerServerGameEndpoints.h | 46 ++++ .../Public/LootLockerServerHttpClient.h | 26 ++ .../Public/LootLockerServerManager.h | 177 ++++++++++++ .../Public/LootLockerServerSDK.h | 15 + .../Public/LootLockerServerSDKManager.h | 211 ++++++++++++++ .../Public/LootLockerSrvPersitentDataHolder.h | 25 ++ .../ServerAPI/LootLockerServerAssetRequest.h | 260 ++++++++++++++++++ .../ServerAPI/LootLockerServerAuthRequest.h | 51 ++++ .../LootLockerServerCharacterRequest.h | 125 +++++++++ .../ServerAPI/LootLockerServerHeroesRequest.h | 137 +++++++++ .../ServerAPI/LootLockerServerPlayerRequest.h | 146 ++++++++++ .../LootLockerServerStorageRequest.h | 115 ++++++++ .../LootLockerServerTriggerRequest.h | 67 +++++ 104 files changed, 6739 insertions(+) create mode 100644 .gitignore create mode 100644 4.26/DemoProject/Config/DefaultEditor.ini create mode 100644 4.26/DemoProject/Config/DefaultEngine.ini create mode 100644 4.26/DemoProject/Config/DefaultGame.ini create mode 100644 4.26/DemoProject/Config/DefaultLootLockerSDK.ini create mode 100644 4.26/DemoProject/Config/DefaultLootLockerServerSDK.ini create mode 100644 4.26/DemoProject/Content/ServerDemo/LootLockerServerDemo.umap create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoAssets.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoAuth.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoCase.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoCharacters.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoHeroes.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoLootLockerServer.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoPlayerInventory.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoPlayerPersistentStorage.uasset create mode 100644 4.26/DemoProject/Content/ServerDemo/WBP_DemoTriggers.uasset create mode 100644 4.26/DemoProject/DemoProject.uproject create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Resources/Icon128.png create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h create mode 100644 4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h create mode 100644 4.26/DemoProject/Source/DemoProject.Target.cs create mode 100644 4.26/DemoProject/Source/DemoProject/DemoProject.Build.cs create mode 100644 4.26/DemoProject/Source/DemoProject/DemoProject.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/DemoProject.h create mode 100644 4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.h create mode 100644 4.26/DemoProject/Source/DemoProject/Private/DemoAssets.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/Private/DemoAuth.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/Private/DemoCharacters.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/Private/DemoHeroes.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/Private/DemoPlayerInventory.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/Private/DemoPlayerPersistentStorage.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/Private/DemoTriggers.cpp create mode 100644 4.26/DemoProject/Source/DemoProject/Public/DemoAssets.h create mode 100644 4.26/DemoProject/Source/DemoProject/Public/DemoAuth.h create mode 100644 4.26/DemoProject/Source/DemoProject/Public/DemoCharacters.h create mode 100644 4.26/DemoProject/Source/DemoProject/Public/DemoHeroes.h create mode 100644 4.26/DemoProject/Source/DemoProject/Public/DemoPlayerInventory.h create mode 100644 4.26/DemoProject/Source/DemoProject/Public/DemoPlayerPersistentStorage.h create mode 100644 4.26/DemoProject/Source/DemoProject/Public/DemoTriggers.h create mode 100644 4.26/DemoProject/Source/DemoProjectEditor.Target.cs create mode 100644 4.26/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin create mode 100644 4.26/Plugins/LootLockerServerSDK/Resources/Icon128.png create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h create mode 100644 4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d028805 --- /dev/null +++ b/.gitignore @@ -0,0 +1,79 @@ +# JetBrains IDEs +.idea/ + +# Visual Studio 2015 user specific files +.vs/ + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +*.ipa + +# These project files can be generated by the engine +*.xcodeproj +*.xcworkspace +*.sln +*.suo +*.opensdf +*.sdf +*.VC.db +*.VC.opendb + +# Precompiled Assets +SourceArt/**/*.png +SourceArt/**/*.tga + +# Binary Files +DemoProject/Binaries/* +DemoProject/Plugins/*/Binaries/* +Plugins/*/Binaries/* + +# Builds +Build/* + +# Whitelist PakBlacklist-.txt files +!Build/*/ +DemoProject/Build/*/** +!Build/*/PakBlacklist*.txt + +# Don't ignore icon files in Build +!Build/**/*.ico + +# Built data for maps +*_BuiltData.uasset + +# Configuration files generated by the Editor +DemoProject/Saved/* + +# Compiled source files for the engine to use +DemoProject/Intermediate/* +DemoProject/Plugins/*/Intermediate/* +Plugins/*/Intermediate/* + +# Cache files for the editor to use +DemoProject/DerivedDataCache/* diff --git a/4.26/DemoProject/Config/DefaultEditor.ini b/4.26/DemoProject/Config/DefaultEditor.ini new file mode 100644 index 0000000..e69de29 diff --git a/4.26/DemoProject/Config/DefaultEngine.ini b/4.26/DemoProject/Config/DefaultEngine.ini new file mode 100644 index 0000000..2ac2053 --- /dev/null +++ b/4.26/DemoProject/Config/DefaultEngine.ini @@ -0,0 +1,36 @@ + + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/DemoProject") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/DemoProject") ++ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="DemoProjectGameModeBase") + +[CoreRedirects] ++ClassRedirects=(OldName="/LootLockerSDK/Demo/GameUI/DemoLootLocker",NewName="/Game/Demo/GameUI/DemoLootLocker") + +[/Script/EngineSettings.GameMapsSettings] +EditorStartupMap=/Game/ServerDemo/LootLockerServerDemo.LootLockerServerDemo +GameDefaultMap=/Game/ServerDemo/LootLockerServerDemo.LootLockerServerDemo + +[/Script/Engine.GameEngine] ++NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver") + +[OnlineSubsystem] +DefaultPlatformService=Steam + +[OnlineSubsystemSteam] +bEnabled=true +SteamDevAppId=480 + +[/Script/OnlineSubsystemSteam.SteamNetDriver] +NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection" + +[PacketHandlerComponents] ++Components=OnlineSubsystemSteam.SteamAuthComponentModuleInterface + diff --git a/4.26/DemoProject/Config/DefaultGame.ini b/4.26/DemoProject/Config/DefaultGame.ini new file mode 100644 index 0000000..3083925 --- /dev/null +++ b/4.26/DemoProject/Config/DefaultGame.ini @@ -0,0 +1,7 @@ + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=8AB46C7744697EDB0C027084D276EC34 +Description=LootLocker Demo Project +ProjectName=LootLocker Demo Project +CompanyName=LootLocker + diff --git a/4.26/DemoProject/Config/DefaultLootLockerSDK.ini b/4.26/DemoProject/Config/DefaultLootLockerSDK.ini new file mode 100644 index 0000000..65cbceb --- /dev/null +++ b/4.26/DemoProject/Config/DefaultLootLockerSDK.ini @@ -0,0 +1,7 @@ +[/Script/LootLockerSDK.LootLockerConfig] +LootLockerGameKey=d68e80882e497af07338bdff0aa9822c9e609a68 +Platform=Steam +GameVersion=1.0.0.0 +OnDevelopmentMode=True +AllowTokenRefresh=False + diff --git a/4.26/DemoProject/Config/DefaultLootLockerServerSDK.ini b/4.26/DemoProject/Config/DefaultLootLockerServerSDK.ini new file mode 100644 index 0000000..2167c5e --- /dev/null +++ b/4.26/DemoProject/Config/DefaultLootLockerServerSDK.ini @@ -0,0 +1,6 @@ +[/Script/LootLockerServerSDK.LootLockerServerConfig] +LootLockerServerKey=d68e80882e497af07338bdff0aa9822c9e609a68 +OnDevelopmentMode=True +GameVersion=1.0.0.0 +LootLockerVersion=2021-03-01 + diff --git a/4.26/DemoProject/Content/ServerDemo/LootLockerServerDemo.umap b/4.26/DemoProject/Content/ServerDemo/LootLockerServerDemo.umap new file mode 100644 index 0000000000000000000000000000000000000000..2e18b466023288bc28d6be752a287d9c86153e31 GIT binary patch literal 26331 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 W#ANh$2z}s57<@)3@ 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 VU^~l22lyFHhnDgqryp|N{{ss6*)aeB literal 0 HcmV?d00001 diff --git a/4.26/DemoProject/Content/ServerDemo/WBP_DemoCase.uasset b/4.26/DemoProject/Content/ServerDemo/WBP_DemoCase.uasset new file mode 100644 index 0000000000000000000000000000000000000000..13413f0f429a6f721c526f1a8aabdfdcac024a97 GIT binary patch literal 46046 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 Lmh||t;oJWQe3&wn literal 0 HcmV?d00001 diff --git a/4.26/DemoProject/Content/ServerDemo/WBP_DemoPlayerInventory.uasset b/4.26/DemoProject/Content/ServerDemo/WBP_DemoPlayerInventory.uasset new file mode 100644 index 0000000000000000000000000000000000000000..9ca69ed18c4ca182df1896d5927ea86e3bffdec0 GIT binary patch literal 265883 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 O99+Iw?wWAV;{OL*I&6sm literal 0 HcmV?d00001 diff --git a/4.26/DemoProject/Content/ServerDemo/WBP_DemoPlayerPersistentStorage.uasset b/4.26/DemoProject/Content/ServerDemo/WBP_DemoPlayerPersistentStorage.uasset new file mode 100644 index 0000000000000000000000000000000000000000..32eff93939b111054bb610b2645758bd1607786d GIT binary patch literal 162319 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 W%7oJR5%5S4v|@N`w&jV|FZ>?|2C^>z literal 0 HcmV?d00001 diff --git a/4.26/DemoProject/Content/ServerDemo/WBP_DemoTriggers.uasset b/4.26/DemoProject/Content/ServerDemo/WBP_DemoTriggers.uasset new file mode 100644 index 0000000000000000000000000000000000000000..c5934e3d5cfd9c1d276bf858c6814b2ff21c7b7c GIT binary patch literal 81698 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 Q&{0l@k@A`o_P*l(0aA!nVE_OC literal 0 HcmV?d00001 diff --git a/4.26/DemoProject/DemoProject.uproject b/4.26/DemoProject/DemoProject.uproject new file mode 100644 index 0000000..f9d371b --- /dev/null +++ b/4.26/DemoProject/DemoProject.uproject @@ -0,0 +1,22 @@ +{ + "FileVersion": 3, + "EngineAssociation": "4.26", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "DemoProject", + "Type": "Runtime", + "LoadingPhase": "Default", + "AdditionalDependencies": [ + "Engine" + ] + } + ], + "Plugins": [ + { + "Name": "OnlineSubsystemSteam", + "Enabled": true + } + ] +} \ No newline at end of file diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin b/4.26/DemoProject/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin new file mode 100644 index 0000000..45e5293 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin @@ -0,0 +1,24 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "LootLockerServerSDK", + "Description": "Server SDK", + "Category": "Other", + "CreatedBy": "LootLocker", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "LootLockerServerSDK", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Resources/Icon128.png b/4.26/DemoProject/Plugins/LootLockerServerSDK/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..c88b9418bdef9ae38d5ea5d326a540d331f371f8 GIT binary patch literal 4326 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 literal 0 HcmV?d00001 diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs new file mode 100644 index 0000000..cdbb0b6 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs @@ -0,0 +1,53 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class LootLockerServerSDK : ModuleRules +{ + public LootLockerServerSDK(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + PrivateDependencyModuleNames.AddRange(new string[] { "Http", "Json", "JsonUtilities" }); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore" + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp new file mode 100644 index 0000000..f7b4c6e --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerConfig.h" + + +FString ULootLockerServerConfig::GetEnum(const TCHAR* Enum, int32 EnumValue) +{ + const UEnum* EnumPtr = FindObject(ANY_PACKAGE, Enum, true); + if (!EnumPtr) + return NSLOCTEXT("Invalid", "Invalid", "Invalid").ToString(); + + return EnumPtr->GetDisplayNameTextByValue(EnumValue).ToString(); + +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp new file mode 100644 index 0000000..dff67c6 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerGameEndpoints.h" + +#include "LootLockerServerConfig.h" + +FString ULootLockerServerGameEndpoints::GameBaseUrl = "https://api.lootlocker.io/server/"; + +//Auth +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::StartSessionEndpoint = InitEndpoint("session", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::MaintainingSessionEndpoint = InitEndpoint("ping", ELootLockerServerHTTPMethod::GET); + +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetAssetsToGameEndpoint = InitEndpoint("assets", ELootLockerServerHTTPMethod::GET); +//Player Inventory +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerInventoryEndpoint = InitEndpoint("player/{0}/inventory", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::AddAssetsToPlayerInventoryEndpoint = InitEndpoint("player/{0}/inventory", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerLoadoutEndpoint = InitEndpoint("player/{0}/loadout", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::EquipAssetToPlayerLoadoutEndpoint = InitEndpoint("player/{0}/loadout", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UnEquipAssetToPlayerLoadoutEndpoint = InitEndpoint("player/{0}/loadout/{1}", ELootLockerServerHTTPMethod::DELETE); +//Player persisitent storage +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPersistentStorageEndpoint = InitEndpoint("players/storage?player_ids={0}", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UpdatePersistentStorageEndpoint = InitEndpoint("players/storage", ELootLockerServerHTTPMethod::PATCH); +//Characters & Heroes +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerCharactersEndpoint = InitEndpoint("player/{0}/characters", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetInventorytoCharacterEndpoint = InitEndpoint("player/{0}/character/{1}/inventory", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetCharacterLoadoutEndpoint = InitEndpoint("player/{0}/characters/{1}/loadout", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::EquipAssetforCharacterLoadoutEndpoint = InitEndpoint("player/{0}/character/{1}/loadout", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UnequipAssetforCharacterLoadoutEndpoint = InitEndpoint("player/{0}/character/{1}/loadout/{2}", ELootLockerServerHTTPMethod::DELETE); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerHeroesEndpoint = InitEndpoint("player/{0}/heroes", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetInventorytoHeroEndpoint = InitEndpoint("player/{0}/hero/{1}/inventory", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetHeroLoadoutEndpoint = InitEndpoint("player/{0}/hero/{1}/loadout", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::EquipAssetforHeroLoadoutEndpoint = InitEndpoint("player/{0}/hero/{1}/loadout", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UnequipAssetforHeroLoadoutEndpoint = InitEndpoint("player/{0}/hero/{1}/loadout/{2}", ELootLockerServerHTTPMethod::DELETE); +//Trigger +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::InvokeTriggeronBehalfofPlayerEndpoint = InitEndpoint("trigger", ELootLockerServerHTTPMethod::POST); + +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::InitEndpoint(const FString& Endpoint, ELootLockerServerHTTPMethod Method) +{ + FLootLockerServerEndPoints Result; + Result.endpoint = GameBaseUrl + Endpoint; + Result.requestMethod = Method; + return Result; +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp new file mode 100644 index 0000000..06f69a6 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 LootLocker + + +#include "LootLockerServerHttpClient.h" +#include "JsonObjectConverter.h" +#include "LootLockerSrvPersitentDataHolder.h" +#include "Interfaces/IHttpResponse.h" +#include "Misc/FileHelper.h" + +ULootLockerServerHttpClient::ULootLockerServerHttpClient() +{ + +} + +void ULootLockerServerHttpClient::SendApi(const FString& endPoint, const FString& requestType, const FString& data, const FServerResponseCallback& onCompleteRequest, bool useHeader) +{ + FHttpModule* HttpModule = &FHttpModule::Get(); +#if ENGINE_MINOR_VERSION < 26 + TSharedRef Request = HttpModule->CreateRequest(); +#else + TSharedRef Request = HttpModule->CreateRequest(); +#endif + Request->SetURL(endPoint); + + ULootLockerSrvPersitentDataHolder::CachedLastEndpointUsed = endPoint; + ULootLockerSrvPersitentDataHolder::CachedLastRequestTypeUsed = requestType; + ULootLockerSrvPersitentDataHolder::CachedLastDataSentToServer = data; + savedOnCompleteRequest = onCompleteRequest; + + Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent")); + Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + Request->SetHeader(TEXT("Accepts"), TEXT("application/json")); + + const ULootLockerServerConfig* config = GetDefault(); + Request->SetHeader(TEXT("LL-Version"), config->LootLockerVersion); + Request->SetHeader(TEXT("x-server-key"), config->LootLockerServerKey); + + if (useHeader) + { + Request->SetHeader(TEXT("x-auth-token"), ULootLockerSrvPersitentDataHolder::ServerToken); + } + + Request->SetVerb(requestType); + Request->SetContentAsString(data); + + Request->OnProcessRequestComplete().BindLambda([onCompleteRequest, this](FHttpRequestPtr Req, FHttpResponsePtr Response, bool bWasSuccessful) + { + const FString ResponseString = Response->GetContentAsString(); + FLootLockerServerResponse response; + + if (!ResponseIsValid(Response, bWasSuccessful)) + { + response.success = false; + response.FullTextFromServer = Response->GetContentAsString(); + response.ServerCallHasError = true; + response.ServerCallStatusCode = Response->GetResponseCode(); + response.ServerError = Response->GetContentAsString(); + onCompleteRequest.ExecuteIfBound(response); + return; + } + + UE_LOG(LogTemp, Warning, TEXT("Response code: %d; Response content:\n%s"), Response->GetResponseCode(), *ResponseString); + response.success = true; + response.FullTextFromServer = Response->GetContentAsString(); + response.ServerCallHasError = false; + response.ServerCallStatusCode = Response->GetResponseCode(); + response.ServerError = Response->GetContentAsString(); + onCompleteRequest.ExecuteIfBound(response); + }); + Request->ProcessRequest(); +} + +bool ULootLockerServerHttpClient::ResponseIsValid(const FHttpResponsePtr& InResponse, bool bWasSuccessful) +{ + if (!bWasSuccessful || !InResponse.IsValid()) + return false; + + if (EHttpResponseCodes::IsOk(InResponse->GetResponseCode())) + { + return true; + } + else + { + if (InResponse->GetResponseCode() == 401) + { + UE_LOG(LogTemp, Warning, TEXT("Token has expirred")); + return false; + } + UE_LOG(LogTemp, Warning, TEXT("Http Response returned error code: %d"), InResponse->GetResponseCode()); + UE_LOG(LogTemp, Warning, TEXT("Http Response content:\n%s"), *InResponse->GetContentAsString()); + return false; + } +} + +void ULootLockerServerHttpClient::UploadFile(const FString& endPoint, const FString& requestType, const FString& FilePath, const TMap AdditionalFields, const FServerResponseCallback& onCompleteRequest, bool useHeader, bool useAdmin) +{ + FHttpModule* HttpModule = &FHttpModule::Get(); +#if ENGINE_MINOR_VERSION < 26 + TSharedRef Request = HttpModule->CreateRequest(); +#else + TSharedRef Request = HttpModule->CreateRequest(); +#endif + + Request->SetURL(endPoint); + + ULootLockerSrvPersitentDataHolder::CachedLastEndpointUsed = endPoint; + ULootLockerSrvPersitentDataHolder::CachedLastRequestTypeUsed = requestType; + savedOnCompleteRequest = onCompleteRequest; + + FString Boundary = "lootlockerboundary"; + + Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent")); + Request->SetHeader(TEXT("Content-Type"), TEXT("multipart/form-data; boundary=" + Boundary)); + + if (useHeader) + { + if (!useAdmin) { + Request->SetHeader(TEXT("x-session-token"), ULootLockerSrvPersitentDataHolder::Token); + } + else { + Request->SetHeader(TEXT("x-auth-token"), ULootLockerSrvPersitentDataHolder::AdminToken); + } + } + + Request->SetVerb(requestType); + + TArray UpFileRawData; + if (!FFileHelper::LoadFileToArray(UpFileRawData, *FilePath)) { + UE_LOG(LogTemp, Error, TEXT("FILE NOT READ!")); + return; + } + + TArray Data; + + const FString BeginBoundary = TEXT("\r\n--" + Boundary + "\r\n"); + const FString EndBoundary = TEXT("\r\n--" + Boundary + "--\r\n"); + + for (auto KeyValuePair : AdditionalFields) { + Data.Append((uint8*)TCHAR_TO_ANSI(*BeginBoundary), BeginBoundary.Len()); + + FString ParameterEntry = "Content-Type: text/plain; charset=\"utf-8\"\r\n"; + ParameterEntry.Append(TEXT("Content-Disposition: form-data; name=\"")); + ParameterEntry.Append(KeyValuePair.Key); + ParameterEntry.Append(TEXT("\"\r\n\r\n")); + ParameterEntry.Append(KeyValuePair.Value); + + Data.Append((uint8*)TCHAR_TO_ANSI(*ParameterEntry), ParameterEntry.Len()); + } + + Data.Append((uint8*)TCHAR_TO_ANSI(*BeginBoundary), BeginBoundary.Len()); + + FString FileHeader = (TEXT("Content-Type: application/octet-stream\r\n")); + FileHeader.Append(TEXT("Content-disposition: form-data; name=\"file\"; filename=\"")); + + int32 LastSlashPos; + FilePath.FindLastChar('/', LastSlashPos); + FString FileName = FilePath.RightChop(LastSlashPos + 1); + + FileHeader.Append(FileName + "\"\r\n\r\n"); + + Data.Append((uint8*)TCHAR_TO_ANSI(*FileHeader), FileHeader.Len()); + Data.Append(UpFileRawData); + Data.Append((uint8*)TCHAR_TO_ANSI(*EndBoundary), EndBoundary.Len()); + + Request->SetContent(Data); + + Request->OnProcessRequestComplete().BindLambda([onCompleteRequest, this](FHttpRequestPtr Req, FHttpResponsePtr Response, bool bWasSuccessful) + { + const FString ResponseString = Response->GetContentAsString(); + FLootLockerServerResponse response; + + response.FullTextFromServer = Response->GetContentAsString(); + response.ServerCallStatusCode = Response->GetResponseCode(); + response.ServerError = Response->GetContentAsString(); + + UE_LOG(LogTemp, Warning, TEXT("Response code: %d; Response content:\n%s"), Response->GetResponseCode(), *ResponseString); + bool success = ResponseIsValid(Response, bWasSuccessful); + + response.success = success; + response.ServerCallHasError = !success; + + onCompleteRequest.ExecuteIfBound(response); + }); + Request->ProcessRequest(); +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp new file mode 100644 index 0000000..0487b5e --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp @@ -0,0 +1,123 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerManager.h" + +void ULootLockerServerManager::StartSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted) +{ + ULootLockerServerAuthRequest::StartSession(OnStartedSessionRequestCompleted); +} +void ULootLockerServerManager::MaintainSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted) +{ + ULootLockerServerAuthRequest::MaintainSession(OnStartedSessionRequestCompleted); +} + +void ULootLockerServerManager::GetAssetsToGame(const FServerAssetsResponseDelegateBP& OnGetAssetsRequestCompleted, + int StartFromIndex, int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC) +{ + ULootLockerServerAssetRequest::GetAssetsToGame(StartFromIndex, ItemsCount, AssetFilter, IncludeUGC, OnGetAssetsRequestCompleted, FServerAssetsResponseDelegate()); +} + +void ULootLockerServerManager::GetInventory(const FInventoryResponseBP& OnGetInventoryRequestCompleted, int PlayerId, int StartFromIndex, int ItemsCount) +{ + ULootLockerServerPlayerRequest::GetInventory(PlayerId, StartFromIndex, ItemsCount, OnGetInventoryRequestCompleted, FInventoryResponse()); +} + +void ULootLockerServerManager::AddAssetToPlayerInventory(const FAddAssetResponseBP& OnAddAssetRequestCompleted, + int32 PlayerId, FLootLockerServerAddAssetData AddAssetData) +{ + ULootLockerServerPlayerRequest::AddAssetToPlayerInventory(PlayerId, AddAssetData, OnAddAssetRequestCompleted, FAddAssetResponse()); +} + +void ULootLockerServerManager::GetPlayerLoadout(const FGetPlayerLoadoutResponseBP& OnGetPlayerLoadoutRequestCompleted, + int PlayerId) +{ + ULootLockerServerPlayerRequest::GetPlayerLoadout(PlayerId, OnGetPlayerLoadoutRequestCompleted, FGetPlayerLoadoutResponse()); +} + +void ULootLockerServerManager::EquipAssetForPlayerLoadout(const FEquipAssetResponseBP& OnRequestCompleted, int PlayerId, + int InstanceId) +{ + ULootLockerServerPlayerRequest::EquipAssetForPlayerLoadout(PlayerId, InstanceId, OnRequestCompleted, FEquipAssetResponse()); +} + +void ULootLockerServerManager::UnequipAssetForPlayerLoadout(const FUnequipAssetResponseBP& OnRequestCompleted, + int PlayerId, int LoadoutId) +{ + ULootLockerServerPlayerRequest::UnequipAssetForPlayerLoadout(PlayerId, LoadoutId, OnRequestCompleted, FUnequipAssetResponse()); +} + +void ULootLockerServerManager::GetPersistentStorage(const FGetPersistentStorageResponseBP& OnRequestCompleted, + TArray PlayerIds) +{ + ULootLockerServerStorageRequest::GetPersistentStorage(PlayerIds, OnRequestCompleted, FGetPersistentStorageResponse()); +} + +void ULootLockerServerManager::UpdatePersistentStorage(const FUpdatePersistentStorageResponseBP& OnCompletedRequest, + FLootLockerServerPersistentStorageRequestData RequestData) +{ + ULootLockerServerStorageRequest::UpdatePersistentStorage(RequestData, OnCompletedRequest, FUpdatePersistentStorageResponse()); +} + +void ULootLockerServerManager::GetPlayerCharacters(const FCharactersResponseBP& OnCompletedRequestBP, int PlayerId) +{ + ULootLockerServerCharacterRequest::GetPlayerCharacters(PlayerId, OnCompletedRequestBP, FCharactersResponse()); +} + +void ULootLockerServerManager::GetInventoryToCharacter(const FCharacterInventoryResponseBP& OnCompletedRequestBP, int PlayerId, + int CharacterId) +{ + ULootLockerServerCharacterRequest::GetInventoryToCharacter(PlayerId, CharacterId, OnCompletedRequestBP, FCharacterInventoryResponse()); +} + +void ULootLockerServerManager::GetCharacterLoadout(const FCharacterLoadoutResponseBP& OnCompletedRequestBP, + int PlayerId, int CharacterId) +{ + ULootLockerServerCharacterRequest::GetCharacterLoadout(PlayerId, CharacterId, OnCompletedRequestBP, FCharacterLoadoutResponse()); +} + +void ULootLockerServerManager::EquipAssetForCharacterLoadout(const FEquipResponseBP& OnCompletedRequestBP, int PlayerId, + int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::EquipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, OnCompletedRequestBP, FEquipResponse()); +} + +void ULootLockerServerManager::UnequipAssetForCharacterLoadout(const FUnequipResponseBP& OnCompletedRequestBP, + int PlayerId, int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::UnequipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, OnCompletedRequestBP, FUnequipResponse()); +} + +void ULootLockerServerManager::GetPlayerHeroes(const FHeroesResponseBP& OnCompletedRequestBP, int PlayerId) +{ + ULootLockerServerHeroesRequest::GetPlayerHeroes(PlayerId, OnCompletedRequestBP, FHeroesResponse()); +} + +void ULootLockerServerManager::GetInventoryToHero(const FHeroInventoryResponseBP& OnCompletedRequestBP, int PlayerId, + int HeroId) +{ + ULootLockerServerHeroesRequest::GetInventoryToHero(PlayerId, HeroId, OnCompletedRequestBP, FHeroInventoryResponse()); +} + +void ULootLockerServerManager::GetHeroLoadout(const FHeroLoadoutResponseBP& OnCompletedRequestBP, + int PlayerId, int HeroId) +{ + ULootLockerServerHeroesRequest::GetHeroLoadout(PlayerId, HeroId, OnCompletedRequestBP, FHeroLoadoutResponse()); +} + +void ULootLockerServerManager::EquipAssetForHeroLoadout(const FEquipHeroResponseBP& OnCompletedRequestBP, int PlayerId, + int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::EquipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, OnCompletedRequestBP, FEquipHeroResponse()); +} + +void ULootLockerServerManager::UnequipAssetForHeroLoadout(const FUnequipHeroResponseBP& OnCompletedRequestBP, + int PlayerId, int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::UnequipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, OnCompletedRequestBP, FUnequipHeroResponse()); +} + +void ULootLockerServerManager::InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponseBP& OnCompletedRequestBP, + FString Name, int PlayerId) +{ + ULootLockerServerTriggerRequest::InvokeTriggerOnBehalfOfPlayer(Name, PlayerId, OnCompletedRequestBP, FInvokeTriggerResponse()); +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp new file mode 100644 index 0000000..2a1aeac --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp @@ -0,0 +1,42 @@ +// Copyright Epic Games, Inc. All Rights Reserved. +#include "LootLockerServerSDK.h" +#if WITH_EDITOR +#include "ISettingsModule.h" +#include "ISettingsSection.h" +#include "LootLockerServerConfig.h" +#endif +#include "UObject/ConstructorHelpers.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + + + +#define LOCTEXT_NAMESPACE "FLootLockerServerSDKModule" + +void FLootLockerServerSDKModule::StartupModule() +{ +#if WITH_EDITOR + + ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); + + if (SettingsModule != nullptr) + { + ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "Plugins", "LootLocker Server", + LOCTEXT("LootLockerSDKSettingsName", "LootLockerServerSDK"), + LOCTEXT("LootLockerSDKSettingsDescription", "Configure LootLockerServer SDK."), + GetMutableDefault() + ); + } + +#endif + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FLootLockerServerSDKModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FLootLockerServerSDKModule, LootLockerServerSDK) \ No newline at end of file diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp new file mode 100644 index 0000000..2b63570 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2021 LootLocker + + +#include "LootLockerServerSDKManager.h" + +//Authentication +void ULootLockerServerSDKManager::StartSession(const FServerAuthResponse& OnCompleteRequest) +{ + ULootLockerServerAuthRequest::StartSession( FServerAuthResponseBP(), OnCompleteRequest); +} + +void ULootLockerServerSDKManager::MaintainSession(const FServerAuthResponse& OnCompleteRequest) +{ + ULootLockerServerAuthRequest::MaintainSession(FServerAuthResponseBP(), OnCompleteRequest); +} + +void ULootLockerServerSDKManager::GetAssetsToGame(const FServerAssetsResponseDelegate& OnCompletedRequest, int StartFromIndex, + int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC) +{ + ULootLockerServerAssetRequest::GetAssetsToGame(StartFromIndex, ItemsCount, AssetFilter, IncludeUGC, FServerAssetsResponseDelegateBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetInventory(const FInventoryResponse& OnGetInventoryRequestCompleted, + int PlayerId, int StartFromIndex, int ItemsCount) +{ + ULootLockerServerPlayerRequest::GetInventory(PlayerId, StartFromIndex, ItemsCount, FInventoryResponseBP(), OnGetInventoryRequestCompleted); +} + +void ULootLockerServerSDKManager::AddAssetToPlayerInventory(const FAddAssetResponse& OnAddAssetRequestCompleted, + int PlayerId, FLootLockerServerAddAssetData AddAssetData) +{ + ULootLockerServerPlayerRequest::AddAssetToPlayerInventory(PlayerId, AddAssetData, FAddAssetResponseBP(), OnAddAssetRequestCompleted); +} + +void ULootLockerServerSDKManager::GetPlayerLoadout(const FGetPlayerLoadoutResponse& OnGetPlayerLoadoutRequestCompleted, + int PlayerId) +{ + ULootLockerServerPlayerRequest::GetPlayerLoadout(PlayerId, FGetPlayerLoadoutResponseBP(), OnGetPlayerLoadoutRequestCompleted); +} + +void ULootLockerServerSDKManager::EquipAssetForPlayerLoadout(const FEquipAssetResponse& OnRequestCompleted, + int PlayerId, int InstanceId) +{ + ULootLockerServerPlayerRequest::EquipAssetForPlayerLoadout(PlayerId, InstanceId, FEquipAssetResponseBP(), OnRequestCompleted); +} + +void ULootLockerServerSDKManager::UnequipAssetForPlayerLoadout(const FUnequipAssetResponse& OnRequestCompleted, + int PlayerId, int LoadoutId) +{ + ULootLockerServerPlayerRequest::UnequipAssetForPlayerLoadout(PlayerId, LoadoutId, FUnequipAssetResponseBP(), OnRequestCompleted); +} + +void ULootLockerServerSDKManager::GetPersistentStorage(const FGetPersistentStorageResponse& OnRequestCompleted, + TArray PlayerIds) +{ + ULootLockerServerStorageRequest::GetPersistentStorage(PlayerIds, FGetPersistentStorageResponseBP(), OnRequestCompleted); +} + +void ULootLockerServerSDKManager::UpdatePersistentStorage(const FUpdatePersistentStorageResponse& OnCompletedRequest, + FLootLockerServerPersistentStorageRequestData RequestData) +{ + ULootLockerServerStorageRequest::UpdatePersistentStorage(RequestData, FUpdatePersistentStorageResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetPlayerCharacters(const FCharactersResponse& OnCompletedRequest, int PlayerId) +{ + ULootLockerServerCharacterRequest::GetPlayerCharacters(PlayerId, FCharactersResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetInventoryToCharacter(const FCharacterInventoryResponse& OnCompletedRequest, + int PlayerId, int CharacterId) +{ + ULootLockerServerCharacterRequest::GetInventoryToCharacter(PlayerId, CharacterId, FCharacterInventoryResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetCharacterLoadout(const FCharacterLoadoutResponse& OnCompletedRequest, int PlayerId, + int CharacterId) +{ + ULootLockerServerCharacterRequest::GetCharacterLoadout(PlayerId, CharacterId, FCharacterLoadoutResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::EquipAssetForCharacterLoadout(const FEquipResponse& OnCompletedRequest, + int PlayerId, int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::EquipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, FEquipResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::UnequipAssetForCharacterLoadout(const FUnequipResponse& OnCompletedRequest, + int PlayerId, int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::UnequipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, FUnequipResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetPlayerHeroes(const FHeroesResponse& OnCompletedRequest, int PlayerId) +{ + ULootLockerServerHeroesRequest::GetPlayerHeroes(PlayerId, FHeroesResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetInventoryToHero(const FHeroInventoryResponse& OnCompletedRequest, + int PlayerId, int HeroId) +{ + ULootLockerServerHeroesRequest::GetInventoryToHero(PlayerId, HeroId, FHeroInventoryResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetHeroLoadout(const FHeroLoadoutResponse& OnCompletedRequest, int PlayerId, + int HeroId) +{ + ULootLockerServerHeroesRequest::GetHeroLoadout(PlayerId, HeroId, FHeroLoadoutResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::EquipAssetForHeroLoadout(const FEquipHeroResponse& OnCompletedRequest, + int PlayerId, int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::EquipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, FEquipHeroResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::UnequipAssetForHeroLoadout(const FUnequipHeroResponse& OnCompletedRequest, + int PlayerId, int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::UnequipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, FUnequipHeroResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponse& OnCompletedRequest, + FString Name, int PlayerId) +{ + ULootLockerServerTriggerRequest::InvokeTriggerOnBehalfOfPlayer(Name, PlayerId, FInvokeTriggerResponseBP(), OnCompletedRequest); +} + + diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp new file mode 100644 index 0000000..c86824a --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp @@ -0,0 +1,13 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "LootLockerSrvPersitentDataHolder.h" + +FString ULootLockerSrvPersitentDataHolder::Token = ""; +FString ULootLockerSrvPersitentDataHolder::CachedLastEndpointUsed = ""; +FString ULootLockerSrvPersitentDataHolder::CachedLastRequestTypeUsed = ""; +FString ULootLockerSrvPersitentDataHolder::CachedLastDataSentToServer = ""; +FString ULootLockerSrvPersitentDataHolder::CachedSteamToken = ""; +FString ULootLockerSrvPersitentDataHolder::CachedPlayerIdentifier = ""; +FString ULootLockerSrvPersitentDataHolder::AdminToken = ""; +FString ULootLockerSrvPersitentDataHolder::ServerToken = ""; \ No newline at end of file diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp new file mode 100644 index 0000000..c20faad --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp @@ -0,0 +1,81 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerAssetRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" +#include "LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h" + +ULootLockerServerHttpClient* ULootLockerServerAssetRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerAssetRequest::ULootLockerServerAssetRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerAssetRequest::GetAssetsToGame(int StartFromIndex, int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC, const FServerAssetsResponseDelegateBP& OnCompletedRequestBP, const FServerAssetsResponseDelegate& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetAssetsToGameResponse ResponseStruct; + if (response.success) + { + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + ResponseStruct.success = true; + } + else { + ResponseStruct.success = false; + UE_LOG(LogTemp, Error, TEXT("GetAssets failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetAssetsToGameEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + FString EndpointUrl = Endpoint.endpoint; + if (StartFromIndex != 0) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "after=" + FString::FromInt(StartFromIndex)); + } + if (ItemsCount != 50) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "count=" + FString::FromInt(ItemsCount)); + } + FString Filter = ""; + switch (AssetFilter) + { + case ELootLockerServerAssetFilter::Purchasable: + Filter = "purchasable"; + break; + case ELootLockerServerAssetFilter::NonPurchasable: + Filter = "!purchasable"; + break; + case ELootLockerServerAssetFilter::Rentable: + Filter = "rentable"; + break; + case ELootLockerServerAssetFilter::NonRentable: + Filter = "!rentable"; + break; + case ELootLockerServerAssetFilter::Popular: + Filter = "popular"; + break; + case ELootLockerServerAssetFilter::UnPopular: + Filter = "!popular"; + break; + } + if (!Filter.IsEmpty()) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "filter=" + Filter); + } + + if (IncludeUGC) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "include_ugc=true"); + } + HttpClient->SendApi(EndpointUrl, requestMethod, ContentString, sessionResponse, true); +} + diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp new file mode 100644 index 0000000..2ba419d --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp @@ -0,0 +1,79 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerAuthRequest.h" +#include "LootLockerServerGameEndpoints.h" +#include "LootLockerSrvPersitentDataHolder.h" + + +ULootLockerServerHttpClient* ULootLockerServerAuthRequest::HttpClient = nullptr; + +ULootLockerServerAuthRequest::ULootLockerServerAuthRequest() +{ + HttpClient = NewObject(); +} +// +void ULootLockerServerAuthRequest::StartSession(const FServerAuthResponseBP& OnCompletedRequestBP, const FServerAuthResponse& OnCompletedRequest) +{ + FLootLockerServerAuthenticationRequest authRequest; + const ULootLockerServerConfig* config = GetDefault(); + authRequest.development_mode = config->OnDevelopmentMode; + authRequest.game_version = config->GameVersion; + FString AuthContentString; + FJsonObjectConverter::UStructToJsonObjectString(FLootLockerServerAuthenticationRequest::StaticStruct(), &authRequest, AuthContentString, 0, 0); + + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest, config](FLootLockerServerResponse response) + { + FLootLockerServerAuthenticationResponse ResponseStruct; + if (response.success) + { + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + ULootLockerSrvPersitentDataHolder::ServerToken = ResponseStruct.token; + ResponseStruct.success = true; + } + else + { + ResponseStruct.success = false; + UE_LOG(LogTemp, Error, TEXT("Starting of Session failed")); + } + + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints endpoint = ULootLockerServerGameEndpoints::StartSessionEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(endpoint.requestMethod)); + HttpClient->SendApi(endpoint.endpoint, requestMethod, AuthContentString, sessionResponse); +} + +void ULootLockerServerAuthRequest::MaintainSession(const FServerAuthResponseBP& OnCompletedRequestBP, const FServerAuthResponse& OnCompletedRequest) +{ + FLootLockerServerAuthenticationRequest authRequest; + const ULootLockerServerConfig* config = GetDefault(); + authRequest.development_mode = config->OnDevelopmentMode; + authRequest.game_version = config->GameVersion; + FString AuthContentString; + FJsonObjectConverter::UStructToJsonObjectString(FLootLockerServerAuthenticationRequest::StaticStruct(), &authRequest, AuthContentString, 0, 0); + + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest, config](FLootLockerServerResponse response) + { + FLootLockerServerAuthenticationResponse ResponseStruct; + if (response.success) + { + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + ResponseStruct.success = true; + } + else + { + ResponseStruct.success = false; + UE_LOG(LogTemp, Error, TEXT("Starting of Session failed")); + } + + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints endpoint = ULootLockerServerGameEndpoints::StartSessionEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(endpoint.requestMethod)); + HttpClient->SendApi(endpoint.endpoint, requestMethod, AuthContentString, sessionResponse,true); +} \ No newline at end of file diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp new file mode 100644 index 0000000..e66c21f --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp @@ -0,0 +1,162 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerCharacterRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerCharacterRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerCharacterRequest::ULootLockerServerCharacterRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerCharacterRequest::GetPlayerCharacters(int PlayerId, + const FCharactersResponseBP& OnCompletedRequestBP, const FCharactersResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPlayerCharactersResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerCharactersEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::GetInventoryToCharacter(int PlayerId, int CharacterId, + const FCharacterInventoryResponseBP& OnCompletedRequestBP, const FCharacterInventoryResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerCharacterInventoryResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetInventorytoCharacterEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::GetCharacterLoadout(int PlayerId, int CharacterId, + const FCharacterLoadoutResponseBP& OnCompletedRequestBP, const FCharacterLoadoutResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetCharacterLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetCharacterLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::EquipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, + const FEquipResponseBP& OnCompletedRequestBP, const FEquipResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerEquipCharacterResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("instance_id")), FString::FromInt(InstanceId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::EquipAssetforCharacterLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::UnequipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, + const FUnequipResponseBP& OnCompletedRequestBP, const FUnequipResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUnequipCharacterResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UnequipAssetforCharacterLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId, InstanceId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp new file mode 100644 index 0000000..d198f5f --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp @@ -0,0 +1,161 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerHeroesRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerHeroesRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerHeroesRequest::ULootLockerServerHeroesRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerHeroesRequest::GetPlayerHeroes(int PlayerId, + const FHeroesResponseBP& OnCompletedRequestBP, const FHeroesResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPlayerHeroesResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerHeroesEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::GetInventoryToHero(int PlayerId, int HeroId, + const FHeroInventoryResponseBP& OnCompletedRequestBP, const FHeroInventoryResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerHeroInventoryResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetInventorytoHeroEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::GetHeroLoadout(int PlayerId, int HeroId, + const FHeroLoadoutResponseBP& OnCompletedRequestBP, const FHeroLoadoutResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetHeroLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetHeroLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::EquipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, + const FEquipHeroResponseBP& OnCompletedRequestBP, const FEquipHeroResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerEquipHeroResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("instance_id")), FString::FromInt(InstanceId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::EquipAssetforHeroLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::UnequipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, + const FUnequipHeroResponseBP& OnCompletedRequestBP, const FUnequipHeroResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUnequipHeroResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UnequipAssetforHeroLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId, InstanceId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} \ No newline at end of file diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp new file mode 100644 index 0000000..9528cdd --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp @@ -0,0 +1,168 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerPlayerRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerPlayerRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerPlayerRequest::ULootLockerServerPlayerRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerPlayerRequest::GetInventory(int PlayerId, int StartFromIndex, int ItemsCount, + const FInventoryResponseBP& OnCompletedRequestBP, const FInventoryResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerInventoryResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerInventoryEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::AddAssetToPlayerInventory(int PlayerId, FLootLockerServerAddAssetData AddAssetData, + const FAddAssetResponseBP& OnCompletedRequestBP, const FAddAssetResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerAddAssetResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + FJsonObjectConverter::UStructToJsonObject(FLootLockerServerAddAssetData::StaticStruct(), &AddAssetData, ItemJson, 0, 0); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::AddAssetsToPlayerInventoryEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::GetPlayerLoadout(int PlayerId, + const FGetPlayerLoadoutResponseBP& OnCompletedRequestBP, const FGetPlayerLoadoutResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPlayerLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::EquipAssetForPlayerLoadout(int PlayerId, int InstanceId, + const FEquipAssetResponseBP& OnCompletedRequestBP, const FEquipAssetResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerEquipAssetForPlayerLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("instance_id")), FString::FromInt(InstanceId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::EquipAssetToPlayerLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::UnequipAssetForPlayerLoadout(int PlayerId, int LoadoutId, + const FUnequipAssetResponseBP& OnCompletedRequestBP, const FUnequipAssetResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUnequipAssetForPlayerLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UnEquipAssetToPlayerLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, LoadoutId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp new file mode 100644 index 0000000..e5370c5 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp @@ -0,0 +1,81 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerStorageRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerStorageRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerStorageRequest::ULootLockerServerStorageRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerStorageRequest::GetPersistentStorage(TArray PlayerIds, + const FGetPersistentStorageResponseBP& OnCompletedRequestBP, + const FGetPersistentStorageResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPersistentStorageResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting players failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPersistentStorageEndpoint; + TArray PlayerStringIds; + for (int Id : PlayerIds) + { + PlayerStringIds.Add(FString::FromInt(Id)); + } + + FString endpoint = FString::Format(*(Endpoint.endpoint), { FString::Join(PlayerStringIds, TEXT(",")) }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerStorageRequest::UpdatePersistentStorage(FLootLockerServerPersistentStorageRequestData requestData, + const FUpdatePersistentStorageResponseBP& OnCompletedRequestBP, + const FUpdatePersistentStorageResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUpdatePersistentStorageResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Failed to update persisten storage")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + FJsonObjectConverter::UStructToJsonObject(FLootLockerServerPersistentStorageRequestData::StaticStruct(), &requestData, ItemJson, 0, 0); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UpdatePersistentStorageEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(Endpoint.endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp new file mode 100644 index 0000000..b6d45e8 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp @@ -0,0 +1,47 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerTriggerRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerTriggerRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerTriggerRequest::ULootLockerServerTriggerRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerTriggerRequest::InvokeTriggerOnBehalfOfPlayer(FString name, int PlayerId, + const FInvokeTriggerResponseBP& OnCompletedRequestBP, const FInvokeTriggerResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerInvokeTriggerResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Invoking trigger failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("name")), name); + ItemJson->SetStringField(FString(TEXT("player_id")), FString::FromInt(PlayerId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::InvokeTriggeronBehalfofPlayerEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(Endpoint.endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp new file mode 100644 index 0000000..9e614ad --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerUtilities.h" + +namespace LootLockerServerUtilities +{ + FString AppendParameterToUrl(const FString& Url, const FString& Parameter) + { + FString AppendSymbol = Url.Contains("?") ? "&" : "?"; + return Url + AppendSymbol + Parameter; + } +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h new file mode 100644 index 0000000..c8f3a36 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h @@ -0,0 +1,8 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +namespace LootLockerServerUtilities +{ + FString AppendParameterToUrl(const FString& Url, const FString& Parameter); +} diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h new file mode 100644 index 0000000..65c8f5c --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h @@ -0,0 +1,69 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "HttpModule.h" +#include "LootLockerServerConfig.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerResponse +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool success; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool ServerCallHasError; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int ServerCallStatusCode; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString FullTextFromServer; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString ServerError; +}; + +UENUM(BlueprintType) +enum class ELootLockerServerHTTPMethod : uint8 +{ + GET = 0 UMETA(DisplayName = "GET"), + POST = 1 UMETA(DisplayName = "POST"), + DELETE = 2 UMETA(DisplayName = "DELETE"), + PUT = 3 UMETA(DisplayName = "PUT"), + HEAD = 4 UMETA(DisplayName = "HEAD"), + CREATE = 5 UMETA(DisplayName = "CREATE"), + OPTIONS = 6 UMETA(DisplayName = "OPTIONS"), + PATCH = 7 UMETA(DisplayName = "PATCH"), + UPLOAD = 8 UMETA(DisplayName = "UPLOAD") +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerEndPoints +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString endpoint; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + ELootLockerServerHTTPMethod requestMethod; +}; + +DECLARE_DELEGATE_OneParam(FServerResponseCallback, FLootLockerServerResponse); + +UCLASS(Config = LootLockerServerSDK) +class LOOTLOCKERSERVERSDK_API ULootLockerServerConfig : public UObject +{ + GENERATED_BODY() +public: + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + FString LootLockerServerKey; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + bool OnDevelopmentMode; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + FString GameVersion; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + FString LootLockerVersion; + + static FString GetEnum(const TCHAR* Enum, int32 EnumValue); +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h new file mode 100644 index 0000000..7ad659b --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h @@ -0,0 +1,46 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "LootLockerServerConfig.h" + +#include "LootLockerServerGameEndpoints.generated.h" + +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerGameEndpoints : public UObject +{ + GENERATED_BODY() +public: + //Auth + static FLootLockerServerEndPoints StartSessionEndpoint; + static FLootLockerServerEndPoints MaintainingSessionEndpoint; + //Assets + static FLootLockerServerEndPoints GetAssetsToGameEndpoint; + //Player Inventory + static FLootLockerServerEndPoints GetPlayerInventoryEndpoint; + static FLootLockerServerEndPoints AddAssetsToPlayerInventoryEndpoint; + static FLootLockerServerEndPoints GetPlayerLoadoutEndpoint; + static FLootLockerServerEndPoints EquipAssetToPlayerLoadoutEndpoint; + static FLootLockerServerEndPoints UnEquipAssetToPlayerLoadoutEndpoint; + //Player persisitent storage + static FLootLockerServerEndPoints GetPersistentStorageEndpoint; + static FLootLockerServerEndPoints UpdatePersistentStorageEndpoint; + //Characters & Heroes + static FLootLockerServerEndPoints GetPlayerCharactersEndpoint; + static FLootLockerServerEndPoints GetInventorytoCharacterEndpoint; + static FLootLockerServerEndPoints GetCharacterLoadoutEndpoint; + static FLootLockerServerEndPoints EquipAssetforCharacterLoadoutEndpoint; + static FLootLockerServerEndPoints UnequipAssetforCharacterLoadoutEndpoint; + static FLootLockerServerEndPoints GetPlayerHeroesEndpoint; + static FLootLockerServerEndPoints GetInventorytoHeroEndpoint; + static FLootLockerServerEndPoints GetHeroLoadoutEndpoint; + static FLootLockerServerEndPoints EquipAssetforHeroLoadoutEndpoint; + static FLootLockerServerEndPoints UnequipAssetforHeroLoadoutEndpoint; + //Trigger + static FLootLockerServerEndPoints InvokeTriggeronBehalfofPlayerEndpoint; + +private: + static FString GameBaseUrl; + + static FLootLockerServerEndPoints InitEndpoint(const FString& Endpoint, ELootLockerServerHTTPMethod Method); +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h new file mode 100644 index 0000000..be116f9 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h @@ -0,0 +1,26 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "Interfaces/IHttpRequest.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.generated.h" + +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerHttpClient : public UObject +{ + GENERATED_BODY() + +public: + ULootLockerServerHttpClient(); + void SendApi(const FString& endPoint, const FString& requestType, const FString& data, const FServerResponseCallback& onCompleteRequest, bool useHeader = false); + void UploadFile(const FString& endPoint, const FString& requestType, const FString& FilePath, const TMap AdditionalFields, const FServerResponseCallback& onCompleteRequest, bool useHeader = false, bool useAdmin = false); +public: + bool ResponseIsValid(const FHttpResponsePtr& InResponse, bool bWasSuccessful); + FServerResponseCallback savedOnCompleteRequest; +}; + + + diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h new file mode 100644 index 0000000..228897b --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h @@ -0,0 +1,177 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "ServerAPI/LootLockerServerAssetRequest.h" +#include "ServerAPI/LootLockerServerAuthRequest.h" +#include "ServerAPI/LootLockerServerCharacterRequest.h" +#include "ServerAPI/LootLockerServerHeroesRequest.h" +#include "ServerAPI/LootLockerServerPlayerRequest.h" +#include "ServerAPI/LootLockerServerStorageRequest.h" +#include "ServerAPI/LootLockerServerTriggerRequest.h" + + + +#include "LootLockerServerManager.generated.h" + +UCLASS(Blueprintable) +class LOOTLOCKERSERVERSDK_API ULootLockerServerManager : public UObject +{ + GENERATED_BODY() + +public: + + //================================================== + //Authentication + //================================================== + + /** + * Register a session. + * + * @param PlayerId - the ID of the player on the platform the game is currently running on. + * https://docs.lootlocker.io/game-api/#authentication-request + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Authentication") + static void StartSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted); + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Authentication") + static void MaintainSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted); + + /** + * Get all assets in a paginated form. + * + * @param StartFromIndex - index of the item to start from. + * @param ItemsCount - number of items to receive (50-200). + * @param AssetFilter - optional filter. + * @param IncludeUGC - whether to include UGC Assets. + * https://docs.lootlocker.io/server-api/#get-assets-to-game + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Assets") + static void GetAssetsToGame(const FServerAssetsResponseDelegateBP& OnGetAssetsRequestCompleted,int StartFromIndex = 0, int ItemsCount = 50, ELootLockerServerAssetFilter AssetFilter = ELootLockerServerAssetFilter::None, bool IncludeUGC = false); + + /** + * Get a paginated list of the players inventory. + * https://docs.lootlocker.io/server-api/#get-player-inventory + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetInventory(const FInventoryResponseBP& OnGetInventoryRequestCompleted, int PlayerId, int StartFromIndex = 0, int ItemsCount = 50); + + /** + * Grant an asset to a player as you see fit + * https://docs.lootlocker.io/server-api/#add-asset-to-player-inventory + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void AddAssetToPlayerInventory(const FAddAssetResponseBP& OnAddAssetRequestCompleted, int32 PlayerId, FLootLockerServerAddAssetData AddAssetData); + + /** + * Return the players default characters loadout + * https://docs.lootlocker.io/server-api/#get-player-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPlayerLoadout(const FGetPlayerLoadoutResponseBP& OnGetPlayerLoadoutRequestCompleted, int PlayerId); + + /** + * Equip an asset instance to the players default character + * https://docs.lootlocker.io/server-api/#equip-asset-for-player-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void EquipAssetForPlayerLoadout(const FEquipAssetResponseBP& OnRequestCompleted, int PlayerId, int InstanceId); + + /** + * Unequip an asset instance for the players default character + * https://docs.lootlocker.io/server-api/#unequip-asset-for-player-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UnequipAssetForPlayerLoadout(const FUnequipAssetResponseBP& OnRequestCompleted, int PlayerId, int LoadoutId); + + /** + * Read player storage one or more players + * https://docs.lootlocker.io/server-api/#player-persistent-storage + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPersistentStorage(const FGetPersistentStorageResponseBP& OnRequestCompleted, TArray PlayerIds); + + /** + * Update Persistent Storage + * https://docs.lootlocker.io/server-api/#update-persistent-storage + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UpdatePersistentStorage(const FUpdatePersistentStorageResponseBP& OnCompletedRequest, FLootLockerServerPersistentStorageRequestData RequestData); + + /** + * List characters to a player + * https://docs.lootlocker.io/server-api/#get-player-characters + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPlayerCharacters(const FCharactersResponseBP& OnCompletedRequestBP, int PlayerId); + + /** + * Get the inventory for a specific character belonging to a player + * https://docs.lootlocker.io/server-api/#get-inventory-to-character + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetInventoryToCharacter(const FCharacterInventoryResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId); + + /** + * Get a characters full loadout. + * https://docs.lootlocker.io/server-api/#get-character-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetCharacterLoadout(const FCharacterLoadoutResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId); + + /** + * Equip an asset instance to a specific character + * https://docs.lootlocker.io/server-api/#equip-asset-for-character-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void EquipAssetForCharacterLoadout(const FEquipResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId, int InstanceId); + + /** + * Unequip an asset instance for a character + * https://docs.lootlocker.io/server-api/#unequip-asset-for-character-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UnequipAssetForCharacterLoadout(const FUnequipResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId, int InstanceId); + + /** + * List heroes to a player + * https://docs.lootlocker.io/server-api/#get-player-heroes + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPlayerHeroes(const FHeroesResponseBP& OnCompletedRequestBP, int PlayerId); + + /** + * Get the inventory for a specific hero belonging to a player + * https://docs.lootlocker.io/server-api/#get-inventory-to-hero + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetInventoryToHero(const FHeroInventoryResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId); + + /** + * Get a hero full loadout. + * https://docs.lootlocker.io/server-api/#get-hero-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetHeroLoadout(const FHeroLoadoutResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId); + + /** + * Equip an asset instance to a specific hero + * https://docs.lootlocker.io/server-api/#equip-asset-for-hero-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void EquipAssetForHeroLoadout(const FEquipHeroResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId, int InstanceId); + + /** + * Unequip an asset instance for a hero + * https://docs.lootlocker.io/server-api/#unequip-asset-for-hero-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UnequipAssetForHeroLoadout(const FUnequipHeroResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId, int InstanceId); + + /** + * Invoke a trigger on behalf of a player + * https://docs.lootlocker.io/server-api/#invoke-trigger-on-behalf-of-player + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponseBP& OnCompletedRequestBP, FString Name, int PlayerId); +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h new file mode 100644 index 0000000..20f34df --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FLootLockerServerSDKModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h new file mode 100644 index 0000000..8b024a3 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h @@ -0,0 +1,211 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "ServerAPI/LootLockerServerAssetRequest.h" +#include "ServerAPI/LootLockerServerAuthRequest.h" +#include "ServerAPI/LootLockerServerCharacterRequest.h" +#include "ServerAPI/LootLockerServerHeroesRequest.h" +#include "ServerAPI/LootLockerServerPlayerRequest.h" +#include "ServerAPI/LootLockerServerStorageRequest.h" +#include "ServerAPI/LootLockerServerTriggerRequest.h" + + + +#include "LootLockerServerSDKManager.generated.h" + +UCLASS(Blueprintable) +class LOOTLOCKERSERVERSDK_API ULootLockerServerSDKManager : public UObject +{ + GENERATED_BODY() + +public: + + //================================================== + //Authentication + //================================================== + + /** + * Register a session. + * @param PlayerId - the ID of the player on the platform the game is currently running on. + * @param OnCompletedRequest - callback to be invoked with the server response. + * https://docs.lootlocker.io/game-api/#authentication-request + */ + static void StartSession(const FServerAuthResponse& OnCompletedRequest); + + static void MaintainSession(const FServerAuthResponse& OnCompletedRequest); + + /** + * Get all assets in a paginated form. + * + * @param OnCompletedRequest - callback to be invoked with the server response. + * @param StartFromIndex - index of the item to start from. + * @param ItemsCount - number of items to receive (50-200). + * @param AssetFilter - optional filter. + * @param IncludeUGC - whether to include UGC Assets. + * https://docs.lootlocker.io/game-api/#getting-asset-list + */ + static void GetAssetsToGame(const FServerAssetsResponseDelegate& OnCompletedRequest, int StartFromIndex = 0, int ItemsCount = 50, ELootLockerServerAssetFilter AssetFilter = ELootLockerServerAssetFilter::None, bool IncludeUGC = false); + + /** + * Get a paginated list of the players inventory. + * @param OnGetInventoryRequestCompleted - callback to be invoked with the server response + * @param StartFromIndex - index of the item to start from + * @param ItemsCount - number of items to receive (50-200) + * https://docs.lootlocker.io/server-api/#get-player-inventory + */ + static void GetInventory(const FInventoryResponse& OnGetInventoryRequestCompleted, int PlayerId, int StartFromIndex = 0, int ItemsCount = 50); + + /** + * Grant an asset to a player as you see fit + * @param OnAddAssetRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * @param AddAssetData - Data about the asset to be added + * https://docs.lootlocker.io/server-api/#add-asset-to-player-inventory + */ + static void AddAssetToPlayerInventory(const FAddAssetResponse& OnAddAssetRequestCompleted, int PlayerId, FLootLockerServerAddAssetData AddAssetData); + + /** + * Return the players default characters loadout + * @param OnGetPlayerLoadoutRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * https://docs.lootlocker.io/server-api/#get-player-loadout + */ + static void GetPlayerLoadout(const FGetPlayerLoadoutResponse& OnGetPlayerLoadoutRequestCompleted, int PlayerId); + + /** + * Equip an asset instance to the players default character + * @param OnRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * @param InstanceId - Asset id + * https://docs.lootlocker.io/server-api/#equip-asset-for-player-loadout + */ + static void EquipAssetForPlayerLoadout(const FEquipAssetResponse& OnRequestCompleted, int PlayerId, int InstanceId); + + /** + * Unequip an asset instance for the players default character + * @param OnRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * @param LoadoutId - Asset id + * https://docs.lootlocker.io/server-api/#equip-asset-for-player-loadout + */ + static void UnequipAssetForPlayerLoadout(const FUnequipAssetResponse& OnRequestCompleted, int PlayerId, int LoadoutId); + + /** + * Read player storage one or more players + * @param OnRequestCompleted - callback to be invoked with the server response + * @param PlayerIds - array of player ids + * https://docs.lootlocker.io/server-api/#player-persistent-storage + */ + static void GetPersistentStorage(const FGetPersistentStorageResponse& OnRequestCompleted, TArray PlayerIds); + + /** + * Update Persistent Storage + * @param OnCompletedRequest - callback to be invoked with the server response + * @param RequestData - payload + * https://docs.lootlocker.io/server-api/#update-persistent-storage + */ + static void UpdatePersistentStorage(const FUpdatePersistentStorageResponse& OnCompletedRequest, FLootLockerServerPersistentStorageRequestData RequestData); + + /** + * List characters to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * https://docs.lootlocker.io/server-api/#get-player-characters + */ + static void GetPlayerCharacters(const FCharactersResponse& OnCompletedRequest, int PlayerId); + + /** + * Get the inventory for a specific character belonging to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * https://docs.lootlocker.io/server-api/#get-inventory-to-character + */ + static void GetInventoryToCharacter(const FCharacterInventoryResponse& OnCompletedRequest, int PlayerId, int CharacterId); + + /** + * Get a characters full loadout. + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * https://docs.lootlocker.io/server-api/#get-character-loadout + */ + static void GetCharacterLoadout(const FCharacterLoadoutResponse& OnCompletedRequest, int PlayerId, int CharacterId); + + /** + * Equip an asset instance to a specific character + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#equip-asset-for-character-loadout + */ + static void EquipAssetForCharacterLoadout(const FEquipResponse& OnCompletedRequest, int PlayerId, int CharacterId, int InstanceId); + + /** + * Unequip an asset instance for a character + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#unequip-asset-for-character-loadout + */ + static void UnequipAssetForCharacterLoadout(const FUnequipResponse& OnCompletedRequest, int PlayerId, int CharacterId, int InstanceId); + + /** + * List heroes to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * https://docs.lootlocker.io/server-api/#get-player-heroes + */ + static void GetPlayerHeroes(const FHeroesResponse& OnCompletedRequest, int PlayerId); + + /** + * Get the inventory for a specific hero belonging to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * https://docs.lootlocker.io/server-api/#get-inventory-to-hero + */ + static void GetInventoryToHero(const FHeroInventoryResponse& OnCompletedRequest, int PlayerId, int HeroId); + + /** + * Get a heroes full loadout. + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * https://docs.lootlocker.io/server-api/#get-hero-loadout + */ + static void GetHeroLoadout(const FHeroLoadoutResponse& OnCompletedRequest, int PlayerId, int HeroId); + + /** + * Equip an asset instance to a specific hero + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#equip-asset-for-hero-loadout + */ + static void EquipAssetForHeroLoadout(const FEquipHeroResponse& OnCompletedRequest, int PlayerId, int HeroId, int InstanceId); + + /** + * Unequip an asset instance for a hero + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#unequip-asset-for-hero-loadout + */ + static void UnequipAssetForHeroLoadout(const FUnequipHeroResponse& OnCompletedRequest, int PlayerId, int HeroId, int InstanceId); + + /** + * Invoke a trigger on behalf of a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param Name - trigger name + * @param PlayerId - player identifier + * https://docs.lootlocker.io/server-api/#invoke-trigger-on-behalf-of-player + */ + static void InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponse& OnCompletedRequest, FString Name, int PlayerId); +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h new file mode 100644 index 0000000..ea81c2d --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerSrvPersitentDataHolder.generated.h" + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerSrvPersitentDataHolder : public UObject +{ + GENERATED_BODY() + public: + static FString Token; + static FString ServerToken; + static FString CachedLastEndpointUsed; + static FString CachedLastRequestTypeUsed; + static FString CachedLastDataSentToServer; + static FString CachedSteamToken; + static FString CachedPlayerIdentifier; + static FString AdminToken; +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h new file mode 100644 index 0000000..117cd8f --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h @@ -0,0 +1,260 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "LootLockerServerHttpClient.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerAssetRequest.generated.h" + +UENUM(BlueprintType) +enum class ELootLockerServerAssetFilter : uint8 +{ + None = 0, + Purchasable = 1, + NonPurchasable = 2, + Rentable = 3, + NonRentable = 4, + Popular = 5, + UnPopular = 6 +}; + +//TODO: implement default loadouts +// USTRUCT(BlueprintType) +// struct FLootLockerServerDefaultLoadouts { +// GENERATED_BODY() +// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") +// bool Light_Vehicle; +// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") +// bool Heavy_Vehicle; +// }; + +USTRUCT(BlueprintType) +struct FLootLockerServerLinks { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString thumbnail; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPersistentStorageItem { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString key; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerRarity { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString short_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString color; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPsn { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString entitlement_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 service_label; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAppStore +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString product_id; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGooglePay +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString product_id; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerExternalIdentifiers { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerPsn psn; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAppStore apple_app_store; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAppStore google_play; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerRentalOption { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 duration; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString sales_price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerLinks links; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerFile { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString url; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray tags; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerFilter { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAssetCandidate +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int created_by_player_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString created_by_player_uid; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerVariation +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString primary_color; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString secondary_color; + //TODO: implement properties + // TArray properties; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerLinks links; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAsset +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool active; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool purchasable; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString sales_price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString display_price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString context; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString unlocks_context; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool detachable; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString updated; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString marked_new; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 default_variation_id; + //TODO: implement default loadouts + // UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + // FLootLockerServerDefaultLoadouts default_loadouts; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString description; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerLinks links; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray storage; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRarity rarity; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool popular; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 popularity_score; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool unique_instance; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerExternalIdentifiers external_identifiers; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray rental_options; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray filters; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray files; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray data_entities; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAssetCandidate asset_candidate; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray variations; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool featured; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool context_locked; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool initially_purchasable; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetAssetsToGameResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FServerAssetsResponseDelegateBP, FLootLockerServerGetAssetsToGameResponse, AssetsResponse); + +DECLARE_DELEGATE_OneParam(FServerAssetsResponseDelegate, FLootLockerServerGetAssetsToGameResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerAssetRequest : public UObject +{ + GENERATED_BODY() + + public: + + ULootLockerServerAssetRequest(); + + static void GetAssetsToGame(int StartFromIndex, int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC, const FServerAssetsResponseDelegateBP& OnCompletedRequestBP = FServerAssetsResponseDelegateBP(), const FServerAssetsResponseDelegate& OnCompletedRequest = FServerAssetsResponseDelegate()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h new file mode 100644 index 0000000..03ff6be --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h @@ -0,0 +1,51 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.h" +#include "JsonObjectConverter.h" +#include "LootLockerServerAuthRequest.generated.h" +/** + * + */ + +USTRUCT(BlueprintType) +struct FLootLockerServerAuthenticationRequest +{ + GENERATED_BODY() + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Startup Item") + FString game_version; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Startup Item") + bool development_mode; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAuthenticationResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString token; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString error; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FServerAuthResponseBP, FLootLockerServerAuthenticationResponse, Var); +DECLARE_DELEGATE_OneParam(FServerAuthResponse, FLootLockerServerAuthenticationResponse); + +UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent)) +class LOOTLOCKERSERVERSDK_API ULootLockerServerAuthRequest : public UObject +{ +public: + GENERATED_BODY() +public: + ULootLockerServerAuthRequest(); + +public: + static void StartSession(const FServerAuthResponseBP& OnCompletedRequestBP = FServerAuthResponseBP(), const FServerAuthResponse& OnCompletedRequest = FServerAuthResponse()); + static void MaintainSession(const FServerAuthResponseBP& OnCompletedRequestBP = FServerAuthResponseBP(), const FServerAuthResponse& OnCompletedRequest = FServerAuthResponse()); + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h new file mode 100644 index 0000000..83b7b4b --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h @@ -0,0 +1,125 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.h" +#include "LootLockerServerPlayerRequest.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerCharacterRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerCharacter +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool default; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerCharacterInventory { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 variation_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString rental_option_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString mounted_at; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString acquisition_source; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRental rental; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerCharacterInventoryResponse : public FLootLockerServerResponse { + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPlayerCharactersResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetCharacterLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT() +struct FLootLockerServerEquipCharacterResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +USTRUCT() +struct FLootLockerServerUnequipCharacterResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FCharactersResponseBP, FLootLockerServerGetPlayerCharactersResponse, CharactersResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FCharacterInventoryResponseBP, FLootLockerServerCharacterInventoryResponse, InventoryResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FCharacterLoadoutResponseBP, FLootLockerServerGetCharacterLoadoutResponse, LoadoutResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FEquipResponseBP, FLootLockerServerEquipCharacterResponse, EquipResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUnequipResponseBP, FLootLockerServerUnequipCharacterResponse, UnequipResponse); +DECLARE_DELEGATE_OneParam(FCharactersResponse, FLootLockerServerGetPlayerCharactersResponse); +DECLARE_DELEGATE_OneParam(FCharacterInventoryResponse, FLootLockerServerCharacterInventoryResponse); +DECLARE_DELEGATE_OneParam(FCharacterLoadoutResponse, FLootLockerServerGetCharacterLoadoutResponse); +DECLARE_DELEGATE_OneParam(FEquipResponse, FLootLockerServerEquipCharacterResponse); +DECLARE_DELEGATE_OneParam(FUnequipResponse, FLootLockerServerUnequipCharacterResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerCharacterRequest : public UObject +{ + GENERATED_BODY() + public: + ULootLockerServerCharacterRequest(); + + static void GetPlayerCharacters(int PlayerId, const FCharactersResponseBP& OnCompletedRequestBP = FCharactersResponseBP(), const FCharactersResponse& OnCompletedRequest = FCharactersResponse()); + + static void GetInventoryToCharacter(int PlayerId, int CharacterId, const FCharacterInventoryResponseBP& OnCompletedRequestBP = FCharacterInventoryResponseBP(), const FCharacterInventoryResponse& OnCompletedRequest = FCharacterInventoryResponse()); + + static void GetCharacterLoadout(int PlayerId, int CharacterId, const FCharacterLoadoutResponseBP& OnCompletedRequestBP = FCharacterLoadoutResponseBP(), const FCharacterLoadoutResponse& OnCompletedRequest = FCharacterLoadoutResponse()); + + static void EquipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, const FEquipResponseBP& OnCompletedRequestBP = FEquipResponseBP(), const FEquipResponse& OnCompletedRequest = FEquipResponse()); + + static void UnequipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, const FUnequipResponseBP& OnCompletedRequestBP = FUnequipResponseBP(), const FUnequipResponse& OnCompletedRequest = FUnequipResponse()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h new file mode 100644 index 0000000..0c1b438 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h @@ -0,0 +1,137 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + + +#include "LootLockerServerAssetRequest.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerPlayerRequest.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerHeroesRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerHero +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int hero_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_default; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString hero_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString character_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString class_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerHeroInventory { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 variation_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString rental_option_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString mounted_at; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString acquisition_source; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRental rental; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerHeroInventoryResponse : public FLootLockerServerResponse { + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPlayerHeroesResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetHeroLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT() +struct FLootLockerServerEquipHeroResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +USTRUCT() +struct FLootLockerServerUnequipHeroResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FHeroesResponseBP, FLootLockerServerGetPlayerHeroesResponse, HeroesResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FHeroInventoryResponseBP, FLootLockerServerHeroInventoryResponse, InventoryResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FHeroLoadoutResponseBP, FLootLockerServerGetHeroLoadoutResponse, LoadoutResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FEquipHeroResponseBP, FLootLockerServerEquipHeroResponse, EquipResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUnequipHeroResponseBP, FLootLockerServerUnequipHeroResponse, UnequipResponse); +DECLARE_DELEGATE_OneParam(FHeroesResponse, FLootLockerServerGetPlayerHeroesResponse); +DECLARE_DELEGATE_OneParam(FHeroInventoryResponse, FLootLockerServerHeroInventoryResponse); +DECLARE_DELEGATE_OneParam(FHeroLoadoutResponse, FLootLockerServerGetHeroLoadoutResponse); +DECLARE_DELEGATE_OneParam(FEquipHeroResponse, FLootLockerServerEquipHeroResponse); +DECLARE_DELEGATE_OneParam(FUnequipHeroResponse, FLootLockerServerUnequipHeroResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerHeroesRequest : public UObject +{ + GENERATED_BODY() + public: + ULootLockerServerHeroesRequest(); + + static void GetPlayerHeroes(int PlayerId, const FHeroesResponseBP& OnCompletedRequestBP = FHeroesResponseBP(), const FHeroesResponse& OnCompletedRequest = FHeroesResponse()); + + static void GetInventoryToHero(int PlayerId, int HeroId, const FHeroInventoryResponseBP& OnCompletedRequestBP = FHeroInventoryResponseBP(), const FHeroInventoryResponse& OnCompletedRequest = FHeroInventoryResponse()); + + static void GetHeroLoadout(int PlayerId, int HeroId, const FHeroLoadoutResponseBP& OnCompletedRequestBP = FHeroLoadoutResponseBP(), const FHeroLoadoutResponse& OnCompletedRequest = FHeroLoadoutResponse()); + + static void EquipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, const FEquipHeroResponseBP& OnCompletedRequestBP = FEquipHeroResponseBP(), const FEquipHeroResponse& OnCompletedRequest = FEquipHeroResponse()); + + static void UnequipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, const FUnequipHeroResponseBP& OnCompletedRequestBP = FUnequipHeroResponseBP(), const FUnequipHeroResponse& OnCompletedRequest = FUnequipHeroResponse()); + + static ULootLockerServerHttpClient* HttpClient; + +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h new file mode 100644 index 0000000..03edbcb --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h @@ -0,0 +1,146 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + + +#include "LootLockerServerAssetRequest.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerPlayerRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerRental { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_rental; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString time_left; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString duration; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString is_active; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerInventory { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 variation_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString rental_option_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString acquisition_source; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRental rental; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerInventoryResponse : public FLootLockerServerResponse { + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAddAssetData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 asset_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 asset_variation_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 asset_rental_option_id; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAddAssetResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPlayerLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerEquipAssetForPlayerLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerUnequipAssetForPlayerLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FInventoryResponseBP, FLootLockerServerInventoryResponse, InventoryResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FAddAssetResponseBP, FLootLockerServerAddAssetResponse, AddAssetResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FGetPlayerLoadoutResponseBP, FLootLockerServerGetPlayerLoadoutResponse, GetPlayerLoadoutResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FEquipAssetResponseBP, FLootLockerServerEquipAssetForPlayerLoadoutResponse, EquipAssetResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUnequipAssetResponseBP, FLootLockerServerUnequipAssetForPlayerLoadoutResponse, UnequipAssetResponse); +DECLARE_DELEGATE_OneParam(FInventoryResponse, FLootLockerServerInventoryResponse); +DECLARE_DELEGATE_OneParam(FAddAssetResponse, FLootLockerServerAddAssetResponse); +DECLARE_DELEGATE_OneParam(FGetPlayerLoadoutResponse, FLootLockerServerGetPlayerLoadoutResponse); +DECLARE_DELEGATE_OneParam(FEquipAssetResponse, FLootLockerServerEquipAssetForPlayerLoadoutResponse); +DECLARE_DELEGATE_OneParam(FUnequipAssetResponse, FLootLockerServerUnequipAssetForPlayerLoadoutResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerPlayerRequest : public UObject +{ + GENERATED_BODY() + +public: + ULootLockerServerPlayerRequest(); + + static void GetInventory(int PlayerId, int StartFromIndex, int ItemsCount, const FInventoryResponseBP& OnCompletedRequestBP = FInventoryResponseBP(), const FInventoryResponse& OnCompletedRequest = FInventoryResponse()); + + static void AddAssetToPlayerInventory(int PlayerId, FLootLockerServerAddAssetData AddAssetData, const FAddAssetResponseBP& OnCompletedRequestBP = FAddAssetResponseBP(), const FAddAssetResponse& OnCompletedRequest = FAddAssetResponse()); + + static void GetPlayerLoadout(int PlayerId, const FGetPlayerLoadoutResponseBP& OnCompletedRequestBP = FGetPlayerLoadoutResponseBP(), const FGetPlayerLoadoutResponse& OnCompletedRequest = FGetPlayerLoadoutResponse()); + + static void EquipAssetForPlayerLoadout(int PlayerId, int InstanceId, const FEquipAssetResponseBP& OnCompletedRequestBP = FEquipAssetResponseBP(), const FEquipAssetResponse& OnCompletedRequest = FEquipAssetResponse()); + + static void UnequipAssetForPlayerLoadout(int PlayerId, int LoadoutId, const FUnequipAssetResponseBP& OnCompletedRequestBP = FUnequipAssetResponseBP(), const FUnequipAssetResponse& OnCompletedRequest = FUnequipAssetResponse()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h new file mode 100644 index 0000000..e03c833 --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h @@ -0,0 +1,115 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "LootLockerServerHttpClient.h" + +#include "LootLockerServerStorageRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerPlayerStorageItemData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString key; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_public; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPlayerStorageItem +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int player_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPersistentStorageResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerKeyValueSet +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString key; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_public; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int order; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPlayerStorageData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int player_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray sets; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPersistentStorageRequestData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray payload; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerUpdatePersistentStorageResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FGetPersistentStorageResponseBP, FLootLockerServerGetPersistentStorageResponse, GetPersistentStorageResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUpdatePersistentStorageResponseBP, FLootLockerServerUpdatePersistentStorageResponse, UpdatePersistentStorageResponse); +DECLARE_DELEGATE_OneParam(FGetPersistentStorageResponse, FLootLockerServerGetPersistentStorageResponse); +DECLARE_DELEGATE_OneParam(FUpdatePersistentStorageResponse, FLootLockerServerUpdatePersistentStorageResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerStorageRequest : public UObject +{ + GENERATED_BODY() + + public: + ULootLockerServerStorageRequest(); + + static void GetPersistentStorage(TArray PlayerIds, const FGetPersistentStorageResponseBP& OnCompletedRequestBP = FGetPersistentStorageResponseBP(), const FGetPersistentStorageResponse& OnCompletedRequest = FGetPersistentStorageResponse()); + static void UpdatePersistentStorage(FLootLockerServerPersistentStorageRequestData requestData, const FUpdatePersistentStorageResponseBP& OnCompletedRequestBP = FUpdatePersistentStorageResponseBP(), const FUpdatePersistentStorageResponse& OnCompletedRequest = FUpdatePersistentStorageResponse()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h new file mode 100644 index 0000000..4fce6fe --- /dev/null +++ b/4.26/DemoProject/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h @@ -0,0 +1,67 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + + +#include "LootLockerServerAssetRequest.h" +#include "LootLockerServerConfig.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerTriggerRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerLevel +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int level; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_prestige; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int xp_threshold; + +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerXP +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int previous; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int current; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerInvokeTriggerResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerXP xp; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray levels; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray granted_assets; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FInvokeTriggerResponseBP, FLootLockerServerInvokeTriggerResponse, InvokeTriggerResponse); +DECLARE_DELEGATE_OneParam(FInvokeTriggerResponse, FLootLockerServerInvokeTriggerResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerTriggerRequest : public UObject +{ + GENERATED_BODY() + public: + ULootLockerServerTriggerRequest(); + + static void InvokeTriggerOnBehalfOfPlayer(FString name, int PlayerId, const FInvokeTriggerResponseBP& OnCompletedRequestBP = FInvokeTriggerResponseBP(), const FInvokeTriggerResponse& OnCompletedRequest = FInvokeTriggerResponse()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/DemoProject/Source/DemoProject.Target.cs b/4.26/DemoProject/Source/DemoProject.Target.cs new file mode 100644 index 0000000..e9a3d6f --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class DemoProjectTarget : TargetRules +{ + public DemoProjectTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Game; + DefaultBuildSettings = BuildSettingsVersion.V2; + ExtraModuleNames.AddRange( new string[] { "DemoProject" } ); + } +} diff --git a/4.26/DemoProject/Source/DemoProject/DemoProject.Build.cs b/4.26/DemoProject/Source/DemoProject/DemoProject.Build.cs new file mode 100644 index 0000000..059a65e --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/DemoProject.Build.cs @@ -0,0 +1,25 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class DemoProject : ModuleRules +{ + public DemoProject(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "LootLockerServerSDK"}); + + PrivateDependencyModuleNames.AddRange(new string[] { "Http", "Json", "JsonUtilities" }); + + PrivateDependencyModuleNames.AddRange(new string[] { }); + + // Uncomment if you are using Slate UI + // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); + + // Uncomment if you are using online features + PrivateDependencyModuleNames.Add("OnlineSubsystem"); + + // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true + } +} diff --git a/4.26/DemoProject/Source/DemoProject/DemoProject.cpp b/4.26/DemoProject/Source/DemoProject/DemoProject.cpp new file mode 100644 index 0000000..7abd7a1 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/DemoProject.cpp @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "DemoProject.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, DemoProject, "DemoProject" ); diff --git a/4.26/DemoProject/Source/DemoProject/DemoProject.h b/4.26/DemoProject/Source/DemoProject/DemoProject.h new file mode 100644 index 0000000..677c8e2 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/DemoProject.h @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + diff --git a/4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.cpp b/4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.cpp new file mode 100644 index 0000000..64d5a9f --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.cpp @@ -0,0 +1,5 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + + +#include "DemoProjectGameModeBase.h" + diff --git a/4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.h b/4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.h new file mode 100644 index 0000000..a4b145b --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/DemoProjectGameModeBase.h @@ -0,0 +1,17 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/GameModeBase.h" +#include "DemoProjectGameModeBase.generated.h" + +/** + * + */ +UCLASS() +class DEMOPROJECT_API ADemoProjectGameModeBase : public AGameModeBase +{ + GENERATED_BODY() + +}; diff --git a/4.26/DemoProject/Source/DemoProject/Private/DemoAssets.cpp b/4.26/DemoProject/Source/DemoProject/Private/DemoAssets.cpp new file mode 100644 index 0000000..4652feb --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Private/DemoAssets.cpp @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DemoAssets.h" + +#include "LootLockerServerSDKManager.h" + +void ADemoAssets::DemoGetAssetsToGame() +{ + ULootLockerServerSDKManager::GetAssetsToGame(FServerAssetsResponseDelegate::CreateUObject(this, &ADemoAssets::OnGetAssetsToGameCompleted), StartFromIndex, ItemsCount, AssetFilter, IncludeUGC); +} + +void ADemoAssets::OnGetAssetsToGameCompleted(FLootLockerServerGetAssetsToGameResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetAssetsToGame Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetAssetsToGame Failed")); + } +} diff --git a/4.26/DemoProject/Source/DemoProject/Private/DemoAuth.cpp b/4.26/DemoProject/Source/DemoProject/Private/DemoAuth.cpp new file mode 100644 index 0000000..cbf9f92 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Private/DemoAuth.cpp @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DemoAuth.h" + +void ADemoAuth::DemoStartSession() +{ + ULootLockerServerSDKManager::StartSession(FServerAuthResponse::CreateUObject(this, &ADemoAuth::OnStartSessionCompleted)); +} + +void ADemoAuth::DemoMaintainSession() +{ + ULootLockerServerSDKManager::MaintainSession(FServerAuthResponse::CreateUObject(this, &ADemoAuth::OnMaintainSessionCompleted)); +} + +void ADemoAuth::OnStartSessionCompleted(FLootLockerServerAuthenticationResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("OnStartSession Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("OnStartSession Failed")); + } +} + +void ADemoAuth::OnMaintainSessionCompleted(FLootLockerServerAuthenticationResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("OnMaintainSession Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("OnMaintainSession Failed")); + } +} diff --git a/4.26/DemoProject/Source/DemoProject/Private/DemoCharacters.cpp b/4.26/DemoProject/Source/DemoProject/Private/DemoCharacters.cpp new file mode 100644 index 0000000..d66122d --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Private/DemoCharacters.cpp @@ -0,0 +1,91 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DemoCharacters.h" + +#include "LootLockerServerSDKManager.h" + +void ADemoCharacters::DemoGetPlayerCharacters() +{ + ULootLockerServerSDKManager::GetPlayerCharacters(FCharactersResponse::CreateUObject(this, &ADemoCharacters::OnGetPlayerCharactersCompleted), PlayerId); +} + +void ADemoCharacters::DemoGetInventoryToCharacter() +{ + ULootLockerServerSDKManager::GetInventoryToCharacter(FCharacterInventoryResponse::CreateUObject(this, &ADemoCharacters::OnGetInventoryToCharacterCompleted), PlayerId, CharacterId); +} + +void ADemoCharacters::DemoGetCharacterLoadout() +{ + ULootLockerServerSDKManager::GetCharacterLoadout(FCharacterLoadoutResponse::CreateUObject(this, &ADemoCharacters::OnGetGetCharacterLoadoutCompleted), PlayerId, CharacterId); +} + +void ADemoCharacters::DemoEquipAssetForCharacterLoadout() +{ + ULootLockerServerSDKManager::EquipAssetForCharacterLoadout(FEquipResponse::CreateUObject(this, &ADemoCharacters::OnEquipAssetForCharacterLoadoutCompleted), PlayerId, CharacterId, AssetId); +} + +void ADemoCharacters::DemoUnEquipAssetForCharacterLoadout() +{ + ULootLockerServerSDKManager::UnequipAssetForCharacterLoadout(FUnequipResponse::CreateUObject(this, &ADemoCharacters::OnUnequipAssetForCharacterLoadoutCompleted), PlayerId, CharacterId, AssetId); +} + +void ADemoCharacters::OnGetPlayerCharactersCompleted(FLootLockerServerGetPlayerCharactersResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerCharacters Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerCharacters Failed")); + } +} + +void ADemoCharacters::OnGetInventoryToCharacterCompleted(FLootLockerServerCharacterInventoryResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetInventoryToCharacter Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetInventoryToCharacter Failed")); + } +} + +void ADemoCharacters::OnGetGetCharacterLoadoutCompleted(FLootLockerServerGetCharacterLoadoutResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetGetCharacterLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetGetCharacterLoadout Failed")); + } +} + +void ADemoCharacters::OnEquipAssetForCharacterLoadoutCompleted(FLootLockerServerEquipCharacterResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("EquipAssetForCharacterLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("EquipAssetForCharacterLoadout Failed")); + } +} + +void ADemoCharacters::OnUnequipAssetForCharacterLoadoutCompleted(FLootLockerServerUnequipCharacterResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("UnequipAssetForCharacterLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("UnequipAssetForCharacterLoadout Failed")); + } +} diff --git a/4.26/DemoProject/Source/DemoProject/Private/DemoHeroes.cpp b/4.26/DemoProject/Source/DemoProject/Private/DemoHeroes.cpp new file mode 100644 index 0000000..bee1266 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Private/DemoHeroes.cpp @@ -0,0 +1,92 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DemoHeroes.h" + +#include "LootLockerServerSDKManager.h" + +void ADemoHeroes::DemoGetPlayerHeroes() +{ + ULootLockerServerSDKManager::GetPlayerHeroes(FHeroesResponse::CreateUObject(this, &ADemoHeroes::OnGetPlayerHeroesCompleted), PlayerId); +} + +void ADemoHeroes::DemoGetInventoryToHero() +{ + ULootLockerServerSDKManager::GetInventoryToHero(FHeroInventoryResponse::CreateUObject(this, &ADemoHeroes::OnGetInventoryToHeroCompleted), PlayerId, HeroId); +} + +void ADemoHeroes::DemoGetHeroLoadout() +{ + ULootLockerServerSDKManager::GetHeroLoadout(FHeroLoadoutResponse::CreateUObject(this, &ADemoHeroes::OnGetGetHeroLoadoutCompleted), PlayerId, HeroId); +} + +void ADemoHeroes::DemoEquipAssetForHeroLoadout() +{ + ULootLockerServerSDKManager::EquipAssetForHeroLoadout(FEquipHeroResponse::CreateUObject(this, &ADemoHeroes::OnEquipAssetForHeroLoadoutCompleted), PlayerId, HeroId, AssetId); +} + +void ADemoHeroes::DemoUnEquipAssetForHeroLoadout() +{ + ULootLockerServerSDKManager::UnequipAssetForHeroLoadout(FUnequipHeroResponse::CreateUObject(this, &ADemoHeroes::OnUnequipAssetForHeroLoadoutCompleted), PlayerId, HeroId, AssetId); +} + +void ADemoHeroes::OnGetPlayerHeroesCompleted(FLootLockerServerGetPlayerHeroesResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerHeroes Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerHeroes Failed")); + } +} + +void ADemoHeroes::OnGetInventoryToHeroCompleted(FLootLockerServerHeroInventoryResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetInventoryToHero Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetInventoryToHero Failed")); + } +} + +void ADemoHeroes::OnGetGetHeroLoadoutCompleted(FLootLockerServerGetHeroLoadoutResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetGetHeroLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetGetHeroLoadout Failed")); + } +} + +void ADemoHeroes::OnEquipAssetForHeroLoadoutCompleted(FLootLockerServerEquipHeroResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("EquipAssetForHeroLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("EquipAssetForHeroLoadout Failed")); + } +} + +void ADemoHeroes::OnUnequipAssetForHeroLoadoutCompleted(FLootLockerServerUnequipHeroResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("UnequipAssetForHeroLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("UnequipAssetForHeroLoadout Failed")); + } +} + diff --git a/4.26/DemoProject/Source/DemoProject/Private/DemoPlayerInventory.cpp b/4.26/DemoProject/Source/DemoProject/Private/DemoPlayerInventory.cpp new file mode 100644 index 0000000..48a1dec --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Private/DemoPlayerInventory.cpp @@ -0,0 +1,97 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DemoPlayerInventory.h" + +#include "LootLockerServerSDKManager.h" + +void ADemoPlayerInventory::DemoGetPlayerInventory() +{ + ULootLockerServerSDKManager::GetInventory(FInventoryResponse::CreateUObject(this, &ADemoPlayerInventory::OnGetPlayerInventoryCompleted), PlayerId, StartFromIndex, ItemsCount); +} + +void ADemoPlayerInventory::DemoAddAssetToPlayerInventory() +{ + FLootLockerServerAddAssetData Data = FLootLockerServerAddAssetData(); + Data.asset_id = AssetId; + Data.asset_rental_option_id = AssetRentalOptionId; + Data.asset_variation_id = AssetVariationId; + ULootLockerServerSDKManager::AddAssetToPlayerInventory(FAddAssetResponse::CreateUObject(this, &ADemoPlayerInventory::OnAddAssetToPlayerInventoryCompleted), PlayerId, Data); +} + +void ADemoPlayerInventory::DemoGetPlayerLoadout() +{ + ULootLockerServerSDKManager::GetPlayerLoadout(FGetPlayerLoadoutResponse::CreateUObject(this, &ADemoPlayerInventory::OnGetPlayerLoadoutCompleted), PlayerId); +} + +void ADemoPlayerInventory::DemoEquipAssetForPlayerLoadout() +{ + ULootLockerServerSDKManager::EquipAssetForPlayerLoadout(FEquipAssetResponse::CreateUObject(this, &ADemoPlayerInventory::OnEquipAssetForPlayerLoadoutCompleted), PlayerId, AssetId); +} + +void ADemoPlayerInventory::DemoUnEquipAssetForPlayerLoadout() +{ + ULootLockerServerSDKManager::UnequipAssetForPlayerLoadout(FUnequipAssetResponse::CreateUObject(this, &ADemoPlayerInventory::OnUnequipAssetForPlayerLoadoutCompleted), PlayerId, AssetId); +} + +void ADemoPlayerInventory::OnGetPlayerInventoryCompleted(FLootLockerServerInventoryResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerInventory Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerInventory Failed")); + } +} + +void ADemoPlayerInventory::OnAddAssetToPlayerInventoryCompleted(FLootLockerServerAddAssetResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("AddAssetToPlayerInventory Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("AddAssetToPlayerInventory Failed")); + } +} + +void ADemoPlayerInventory::OnGetPlayerLoadoutCompleted(FLootLockerServerGetPlayerLoadoutResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetPlayerLoadout Failed")); + } +} + +void ADemoPlayerInventory::OnEquipAssetForPlayerLoadoutCompleted( + FLootLockerServerEquipAssetForPlayerLoadoutResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("EquipAssetForPlayerLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("EquipAssetForPlayerLoadout Failed")); + } +} + +void ADemoPlayerInventory::OnUnequipAssetForPlayerLoadoutCompleted( + FLootLockerServerUnequipAssetForPlayerLoadoutResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("UnEquipAssetForPlayerLoadout Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("UnEquipAssetForPlayerLoadout Failed")); + } +} diff --git a/4.26/DemoProject/Source/DemoProject/Private/DemoPlayerPersistentStorage.cpp b/4.26/DemoProject/Source/DemoProject/Private/DemoPlayerPersistentStorage.cpp new file mode 100644 index 0000000..3ee3442 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Private/DemoPlayerPersistentStorage.cpp @@ -0,0 +1,55 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DemoPlayerPersistentStorage.h" + +#include "LootLockerServerSDKManager.h" + +void ADemoPlayerPersistentStorage::DemoGetPersistentStorage() +{ + TArray PlayerIds; + PlayerIds.Add(PlayerId); + ULootLockerServerSDKManager::GetPersistentStorage(FGetPersistentStorageResponse::CreateUObject(this, &ADemoPlayerPersistentStorage::OnGetPersistentStorageCompleted), PlayerIds); +} + +void ADemoPlayerPersistentStorage::DemoUpdatePersistentStorage() +{ + FLootLockerServerPersistentStorageRequestData RequestData; + FLootLockerServerPlayerStorageData Payload; + FLootLockerServerKeyValueSet Set; + Payload.player_id = PlayerId; + Set.is_public = IsPublic; + Set.key = Key; + Set.value = Value; + Set.order = Order; + Payload.sets = {Set}; + RequestData.payload = {Payload}; + + ULootLockerServerSDKManager::UpdatePersistentStorage(FUpdatePersistentStorageResponse::CreateUObject(this, &ADemoPlayerPersistentStorage::OnUpdatePersistentStorageCompleted), RequestData); +} + +void ADemoPlayerPersistentStorage::OnGetPersistentStorageCompleted( + FLootLockerServerGetPersistentStorageResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("GetPersistentStorage Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("GetPersistentStorage Failed")); + } +} + +void ADemoPlayerPersistentStorage::OnUpdatePersistentStorageCompleted( + FLootLockerServerUpdatePersistentStorageResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("UpdatePersistentStorage Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("UpdatePersistentStorage Failed")); + } +} diff --git a/4.26/DemoProject/Source/DemoProject/Private/DemoTriggers.cpp b/4.26/DemoProject/Source/DemoProject/Private/DemoTriggers.cpp new file mode 100644 index 0000000..4f4c23e --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Private/DemoTriggers.cpp @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "DemoTriggers.h" + +#include "LootLockerServerSDKManager.h" + +void ADemoTriggers::DemoInvokeTriggerOnBehalfOfPlayer() +{ + ULootLockerServerSDKManager::InvokeTriggerOnBehalfOfPlayer(FInvokeTriggerResponse::CreateUObject(this, &ADemoTriggers::OnInvokeTriggerOnBehalfOfPlayerCompleted), TriggerName, PlayerId); +} + +void ADemoTriggers::OnInvokeTriggerOnBehalfOfPlayerCompleted(FLootLockerServerInvokeTriggerResponse Response) +{ + if (Response.success) + { + UE_LOG(LogTemp, Verbose, TEXT("InvokeTriggerOnBehalfOfPlayer Success")); + } + else + { + UE_LOG(LogTemp, Verbose, TEXT("InvokeTriggerOnBehalfOfPlayer Failed")); + } +} + + diff --git a/4.26/DemoProject/Source/DemoProject/Public/DemoAssets.h b/4.26/DemoProject/Source/DemoProject/Public/DemoAssets.h new file mode 100644 index 0000000..8358c9a --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Public/DemoAssets.h @@ -0,0 +1,30 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ServerAPI/LootLockerServerAssetRequest.h" + +#include "DemoAssets.generated.h" + +UCLASS() +class DEMOPROJECT_API ADemoAssets : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int StartFromIndex; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int ItemsCount; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + ELootLockerServerAssetFilter AssetFilter; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool IncludeUGC; + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetAssetsToGame(); + + void OnGetAssetsToGameCompleted(FLootLockerServerGetAssetsToGameResponse Response); +}; diff --git a/4.26/DemoProject/Source/DemoProject/Public/DemoAuth.h b/4.26/DemoProject/Source/DemoProject/Public/DemoAuth.h new file mode 100644 index 0000000..5577b98 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Public/DemoAuth.h @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "LootLockerServerSDKManager.h" +#include "GameFramework/Actor.h" +#include "DemoAuth.generated.h" + +UCLASS() +class DEMOPROJECT_API ADemoAuth : public AActor +{ + GENERATED_BODY() +public: + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoStartSession(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoMaintainSession(); + + void OnStartSessionCompleted(FLootLockerServerAuthenticationResponse Response); + void OnMaintainSessionCompleted(FLootLockerServerAuthenticationResponse Response); + +}; diff --git a/4.26/DemoProject/Source/DemoProject/Public/DemoCharacters.h b/4.26/DemoProject/Source/DemoProject/Public/DemoCharacters.h new file mode 100644 index 0000000..12fa60c --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Public/DemoCharacters.h @@ -0,0 +1,54 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ServerAPI/LootLockerServerCharacterRequest.h" + +#include "DemoCharacters.generated.h" + +UCLASS() +class DEMOPROJECT_API ADemoCharacters : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int StartFromIndex; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int ItemsCount; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int PlayerId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int AssetId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int CharacterId; + + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetPlayerCharacters(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetInventoryToCharacter(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetCharacterLoadout(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoEquipAssetForCharacterLoadout(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoUnEquipAssetForCharacterLoadout(); + + void OnGetPlayerCharactersCompleted(FLootLockerServerGetPlayerCharactersResponse Response); + + void OnGetInventoryToCharacterCompleted(FLootLockerServerCharacterInventoryResponse Response); + + void OnGetGetCharacterLoadoutCompleted(FLootLockerServerGetCharacterLoadoutResponse Response); + + void OnEquipAssetForCharacterLoadoutCompleted(FLootLockerServerEquipCharacterResponse Response); + + void OnUnequipAssetForCharacterLoadoutCompleted(FLootLockerServerUnequipCharacterResponse Response); + +}; diff --git a/4.26/DemoProject/Source/DemoProject/Public/DemoHeroes.h b/4.26/DemoProject/Source/DemoProject/Public/DemoHeroes.h new file mode 100644 index 0000000..54b2d45 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Public/DemoHeroes.h @@ -0,0 +1,54 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ServerAPI/LootLockerServerHeroesRequest.h" + +#include "DemoHeroes.generated.h" + +UCLASS() +class DEMOPROJECT_API ADemoHeroes : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int StartFromIndex; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int ItemsCount; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int PlayerId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int AssetId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int HeroId; + + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetPlayerHeroes(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetInventoryToHero(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetHeroLoadout(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoEquipAssetForHeroLoadout(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoUnEquipAssetForHeroLoadout(); + + void OnGetPlayerHeroesCompleted(FLootLockerServerGetPlayerHeroesResponse Response); + + void OnGetInventoryToHeroCompleted(FLootLockerServerHeroInventoryResponse Response); + + void OnGetGetHeroLoadoutCompleted(FLootLockerServerGetHeroLoadoutResponse Response); + + void OnEquipAssetForHeroLoadoutCompleted(FLootLockerServerEquipHeroResponse Response); + + void OnUnequipAssetForHeroLoadoutCompleted(FLootLockerServerUnequipHeroResponse Response); + +}; diff --git a/4.26/DemoProject/Source/DemoProject/Public/DemoPlayerInventory.h b/4.26/DemoProject/Source/DemoProject/Public/DemoPlayerInventory.h new file mode 100644 index 0000000..3fbb455 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Public/DemoPlayerInventory.h @@ -0,0 +1,56 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ServerAPI/LootLockerServerAssetRequest.h" +#include "ServerAPI/LootLockerServerPlayerRequest.h" + + +#include "DemoPlayerInventory.generated.h" + +UCLASS() +class DEMOPROJECT_API ADemoPlayerInventory : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int StartFromIndex; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int ItemsCount; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int PlayerId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int AssetId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int AssetRentalOptionId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int AssetVariationId; + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetPlayerInventory(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoAddAssetToPlayerInventory(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetPlayerLoadout(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoEquipAssetForPlayerLoadout(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoUnEquipAssetForPlayerLoadout(); + + void OnGetPlayerInventoryCompleted(FLootLockerServerInventoryResponse Response); + + void OnAddAssetToPlayerInventoryCompleted(FLootLockerServerAddAssetResponse Response); + + void OnGetPlayerLoadoutCompleted(FLootLockerServerGetPlayerLoadoutResponse Response); + + void OnEquipAssetForPlayerLoadoutCompleted(FLootLockerServerEquipAssetForPlayerLoadoutResponse Response); + + void OnUnequipAssetForPlayerLoadoutCompleted(FLootLockerServerUnequipAssetForPlayerLoadoutResponse Response); +}; diff --git a/4.26/DemoProject/Source/DemoProject/Public/DemoPlayerPersistentStorage.h b/4.26/DemoProject/Source/DemoProject/Public/DemoPlayerPersistentStorage.h new file mode 100644 index 0000000..1d16231 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Public/DemoPlayerPersistentStorage.h @@ -0,0 +1,38 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ServerAPI/LootLockerServerStorageRequest.h" + +#include "DemoPlayerPersistentStorage.generated.h" + +UCLASS() +class DEMOPROJECT_API ADemoPlayerPersistentStorage : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int PlayerId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int Order; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString Key; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString Value; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool IsPublic; + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoGetPersistentStorage(); + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Authentication") + void DemoUpdatePersistentStorage(); + + void OnGetPersistentStorageCompleted(FLootLockerServerGetPersistentStorageResponse Response); + + void OnUpdatePersistentStorageCompleted(FLootLockerServerUpdatePersistentStorageResponse Response); + +}; diff --git a/4.26/DemoProject/Source/DemoProject/Public/DemoTriggers.h b/4.26/DemoProject/Source/DemoProject/Public/DemoTriggers.h new file mode 100644 index 0000000..83350dd --- /dev/null +++ b/4.26/DemoProject/Source/DemoProject/Public/DemoTriggers.h @@ -0,0 +1,27 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "ServerAPI/LootLockerServerTriggerRequest.h" + +#include "DemoTriggers.generated.h" + +UCLASS() +class DEMOPROJECT_API ADemoTriggers : public AActor +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int PlayerId; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString TriggerName; + + UFUNCTION(BlueprintCallable, CallInEditor, Category = "LootLocker Demo | Assets") + void DemoInvokeTriggerOnBehalfOfPlayer(); + + void OnInvokeTriggerOnBehalfOfPlayerCompleted(FLootLockerServerInvokeTriggerResponse Response); + +}; diff --git a/4.26/DemoProject/Source/DemoProjectEditor.Target.cs b/4.26/DemoProject/Source/DemoProjectEditor.Target.cs new file mode 100644 index 0000000..b2f2742 --- /dev/null +++ b/4.26/DemoProject/Source/DemoProjectEditor.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class DemoProjectEditorTarget : TargetRules +{ + public DemoProjectEditorTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Editor; + DefaultBuildSettings = BuildSettingsVersion.V2; + ExtraModuleNames.AddRange( new string[] { "DemoProject" } ); + } +} diff --git a/4.26/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin b/4.26/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin new file mode 100644 index 0000000..45e5293 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/LootLockerServerSDK.uplugin @@ -0,0 +1,24 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "LootLockerServerSDK", + "Description": "Server SDK", + "Category": "Other", + "CreatedBy": "LootLocker", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "LootLockerServerSDK", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file diff --git a/4.26/Plugins/LootLockerServerSDK/Resources/Icon128.png b/4.26/Plugins/LootLockerServerSDK/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..c88b9418bdef9ae38d5ea5d326a540d331f371f8 GIT binary patch literal 4326 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 literal 0 HcmV?d00001 diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs new file mode 100644 index 0000000..cdbb0b6 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/LootLockerServerSDK.Build.cs @@ -0,0 +1,53 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class LootLockerServerSDK : ModuleRules +{ + public LootLockerServerSDK(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + // ... add other public dependencies that you statically link with here ... + } + ); + PrivateDependencyModuleNames.AddRange(new string[] { "Http", "Json", "JsonUtilities" }); + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore" + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp new file mode 100644 index 0000000..f7b4c6e --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerConfig.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerConfig.h" + + +FString ULootLockerServerConfig::GetEnum(const TCHAR* Enum, int32 EnumValue) +{ + const UEnum* EnumPtr = FindObject(ANY_PACKAGE, Enum, true); + if (!EnumPtr) + return NSLOCTEXT("Invalid", "Invalid", "Invalid").ToString(); + + return EnumPtr->GetDisplayNameTextByValue(EnumValue).ToString(); + +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp new file mode 100644 index 0000000..dff67c6 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerGameEndpoints.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerGameEndpoints.h" + +#include "LootLockerServerConfig.h" + +FString ULootLockerServerGameEndpoints::GameBaseUrl = "https://api.lootlocker.io/server/"; + +//Auth +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::StartSessionEndpoint = InitEndpoint("session", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::MaintainingSessionEndpoint = InitEndpoint("ping", ELootLockerServerHTTPMethod::GET); + +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetAssetsToGameEndpoint = InitEndpoint("assets", ELootLockerServerHTTPMethod::GET); +//Player Inventory +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerInventoryEndpoint = InitEndpoint("player/{0}/inventory", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::AddAssetsToPlayerInventoryEndpoint = InitEndpoint("player/{0}/inventory", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerLoadoutEndpoint = InitEndpoint("player/{0}/loadout", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::EquipAssetToPlayerLoadoutEndpoint = InitEndpoint("player/{0}/loadout", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UnEquipAssetToPlayerLoadoutEndpoint = InitEndpoint("player/{0}/loadout/{1}", ELootLockerServerHTTPMethod::DELETE); +//Player persisitent storage +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPersistentStorageEndpoint = InitEndpoint("players/storage?player_ids={0}", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UpdatePersistentStorageEndpoint = InitEndpoint("players/storage", ELootLockerServerHTTPMethod::PATCH); +//Characters & Heroes +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerCharactersEndpoint = InitEndpoint("player/{0}/characters", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetInventorytoCharacterEndpoint = InitEndpoint("player/{0}/character/{1}/inventory", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetCharacterLoadoutEndpoint = InitEndpoint("player/{0}/characters/{1}/loadout", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::EquipAssetforCharacterLoadoutEndpoint = InitEndpoint("player/{0}/character/{1}/loadout", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UnequipAssetforCharacterLoadoutEndpoint = InitEndpoint("player/{0}/character/{1}/loadout/{2}", ELootLockerServerHTTPMethod::DELETE); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetPlayerHeroesEndpoint = InitEndpoint("player/{0}/heroes", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetInventorytoHeroEndpoint = InitEndpoint("player/{0}/hero/{1}/inventory", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::GetHeroLoadoutEndpoint = InitEndpoint("player/{0}/hero/{1}/loadout", ELootLockerServerHTTPMethod::GET); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::EquipAssetforHeroLoadoutEndpoint = InitEndpoint("player/{0}/hero/{1}/loadout", ELootLockerServerHTTPMethod::POST); +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::UnequipAssetforHeroLoadoutEndpoint = InitEndpoint("player/{0}/hero/{1}/loadout/{2}", ELootLockerServerHTTPMethod::DELETE); +//Trigger +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::InvokeTriggeronBehalfofPlayerEndpoint = InitEndpoint("trigger", ELootLockerServerHTTPMethod::POST); + +FLootLockerServerEndPoints ULootLockerServerGameEndpoints::InitEndpoint(const FString& Endpoint, ELootLockerServerHTTPMethod Method) +{ + FLootLockerServerEndPoints Result; + Result.endpoint = GameBaseUrl + Endpoint; + Result.requestMethod = Method; + return Result; +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp new file mode 100644 index 0000000..06f69a6 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerHttpClient.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2021 LootLocker + + +#include "LootLockerServerHttpClient.h" +#include "JsonObjectConverter.h" +#include "LootLockerSrvPersitentDataHolder.h" +#include "Interfaces/IHttpResponse.h" +#include "Misc/FileHelper.h" + +ULootLockerServerHttpClient::ULootLockerServerHttpClient() +{ + +} + +void ULootLockerServerHttpClient::SendApi(const FString& endPoint, const FString& requestType, const FString& data, const FServerResponseCallback& onCompleteRequest, bool useHeader) +{ + FHttpModule* HttpModule = &FHttpModule::Get(); +#if ENGINE_MINOR_VERSION < 26 + TSharedRef Request = HttpModule->CreateRequest(); +#else + TSharedRef Request = HttpModule->CreateRequest(); +#endif + Request->SetURL(endPoint); + + ULootLockerSrvPersitentDataHolder::CachedLastEndpointUsed = endPoint; + ULootLockerSrvPersitentDataHolder::CachedLastRequestTypeUsed = requestType; + ULootLockerSrvPersitentDataHolder::CachedLastDataSentToServer = data; + savedOnCompleteRequest = onCompleteRequest; + + Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent")); + Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + Request->SetHeader(TEXT("Accepts"), TEXT("application/json")); + + const ULootLockerServerConfig* config = GetDefault(); + Request->SetHeader(TEXT("LL-Version"), config->LootLockerVersion); + Request->SetHeader(TEXT("x-server-key"), config->LootLockerServerKey); + + if (useHeader) + { + Request->SetHeader(TEXT("x-auth-token"), ULootLockerSrvPersitentDataHolder::ServerToken); + } + + Request->SetVerb(requestType); + Request->SetContentAsString(data); + + Request->OnProcessRequestComplete().BindLambda([onCompleteRequest, this](FHttpRequestPtr Req, FHttpResponsePtr Response, bool bWasSuccessful) + { + const FString ResponseString = Response->GetContentAsString(); + FLootLockerServerResponse response; + + if (!ResponseIsValid(Response, bWasSuccessful)) + { + response.success = false; + response.FullTextFromServer = Response->GetContentAsString(); + response.ServerCallHasError = true; + response.ServerCallStatusCode = Response->GetResponseCode(); + response.ServerError = Response->GetContentAsString(); + onCompleteRequest.ExecuteIfBound(response); + return; + } + + UE_LOG(LogTemp, Warning, TEXT("Response code: %d; Response content:\n%s"), Response->GetResponseCode(), *ResponseString); + response.success = true; + response.FullTextFromServer = Response->GetContentAsString(); + response.ServerCallHasError = false; + response.ServerCallStatusCode = Response->GetResponseCode(); + response.ServerError = Response->GetContentAsString(); + onCompleteRequest.ExecuteIfBound(response); + }); + Request->ProcessRequest(); +} + +bool ULootLockerServerHttpClient::ResponseIsValid(const FHttpResponsePtr& InResponse, bool bWasSuccessful) +{ + if (!bWasSuccessful || !InResponse.IsValid()) + return false; + + if (EHttpResponseCodes::IsOk(InResponse->GetResponseCode())) + { + return true; + } + else + { + if (InResponse->GetResponseCode() == 401) + { + UE_LOG(LogTemp, Warning, TEXT("Token has expirred")); + return false; + } + UE_LOG(LogTemp, Warning, TEXT("Http Response returned error code: %d"), InResponse->GetResponseCode()); + UE_LOG(LogTemp, Warning, TEXT("Http Response content:\n%s"), *InResponse->GetContentAsString()); + return false; + } +} + +void ULootLockerServerHttpClient::UploadFile(const FString& endPoint, const FString& requestType, const FString& FilePath, const TMap AdditionalFields, const FServerResponseCallback& onCompleteRequest, bool useHeader, bool useAdmin) +{ + FHttpModule* HttpModule = &FHttpModule::Get(); +#if ENGINE_MINOR_VERSION < 26 + TSharedRef Request = HttpModule->CreateRequest(); +#else + TSharedRef Request = HttpModule->CreateRequest(); +#endif + + Request->SetURL(endPoint); + + ULootLockerSrvPersitentDataHolder::CachedLastEndpointUsed = endPoint; + ULootLockerSrvPersitentDataHolder::CachedLastRequestTypeUsed = requestType; + savedOnCompleteRequest = onCompleteRequest; + + FString Boundary = "lootlockerboundary"; + + Request->SetHeader(TEXT("User-Agent"), TEXT("X-UnrealEngine-Agent")); + Request->SetHeader(TEXT("Content-Type"), TEXT("multipart/form-data; boundary=" + Boundary)); + + if (useHeader) + { + if (!useAdmin) { + Request->SetHeader(TEXT("x-session-token"), ULootLockerSrvPersitentDataHolder::Token); + } + else { + Request->SetHeader(TEXT("x-auth-token"), ULootLockerSrvPersitentDataHolder::AdminToken); + } + } + + Request->SetVerb(requestType); + + TArray UpFileRawData; + if (!FFileHelper::LoadFileToArray(UpFileRawData, *FilePath)) { + UE_LOG(LogTemp, Error, TEXT("FILE NOT READ!")); + return; + } + + TArray Data; + + const FString BeginBoundary = TEXT("\r\n--" + Boundary + "\r\n"); + const FString EndBoundary = TEXT("\r\n--" + Boundary + "--\r\n"); + + for (auto KeyValuePair : AdditionalFields) { + Data.Append((uint8*)TCHAR_TO_ANSI(*BeginBoundary), BeginBoundary.Len()); + + FString ParameterEntry = "Content-Type: text/plain; charset=\"utf-8\"\r\n"; + ParameterEntry.Append(TEXT("Content-Disposition: form-data; name=\"")); + ParameterEntry.Append(KeyValuePair.Key); + ParameterEntry.Append(TEXT("\"\r\n\r\n")); + ParameterEntry.Append(KeyValuePair.Value); + + Data.Append((uint8*)TCHAR_TO_ANSI(*ParameterEntry), ParameterEntry.Len()); + } + + Data.Append((uint8*)TCHAR_TO_ANSI(*BeginBoundary), BeginBoundary.Len()); + + FString FileHeader = (TEXT("Content-Type: application/octet-stream\r\n")); + FileHeader.Append(TEXT("Content-disposition: form-data; name=\"file\"; filename=\"")); + + int32 LastSlashPos; + FilePath.FindLastChar('/', LastSlashPos); + FString FileName = FilePath.RightChop(LastSlashPos + 1); + + FileHeader.Append(FileName + "\"\r\n\r\n"); + + Data.Append((uint8*)TCHAR_TO_ANSI(*FileHeader), FileHeader.Len()); + Data.Append(UpFileRawData); + Data.Append((uint8*)TCHAR_TO_ANSI(*EndBoundary), EndBoundary.Len()); + + Request->SetContent(Data); + + Request->OnProcessRequestComplete().BindLambda([onCompleteRequest, this](FHttpRequestPtr Req, FHttpResponsePtr Response, bool bWasSuccessful) + { + const FString ResponseString = Response->GetContentAsString(); + FLootLockerServerResponse response; + + response.FullTextFromServer = Response->GetContentAsString(); + response.ServerCallStatusCode = Response->GetResponseCode(); + response.ServerError = Response->GetContentAsString(); + + UE_LOG(LogTemp, Warning, TEXT("Response code: %d; Response content:\n%s"), Response->GetResponseCode(), *ResponseString); + bool success = ResponseIsValid(Response, bWasSuccessful); + + response.success = success; + response.ServerCallHasError = !success; + + onCompleteRequest.ExecuteIfBound(response); + }); + Request->ProcessRequest(); +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp new file mode 100644 index 0000000..0487b5e --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerManager.cpp @@ -0,0 +1,123 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerManager.h" + +void ULootLockerServerManager::StartSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted) +{ + ULootLockerServerAuthRequest::StartSession(OnStartedSessionRequestCompleted); +} +void ULootLockerServerManager::MaintainSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted) +{ + ULootLockerServerAuthRequest::MaintainSession(OnStartedSessionRequestCompleted); +} + +void ULootLockerServerManager::GetAssetsToGame(const FServerAssetsResponseDelegateBP& OnGetAssetsRequestCompleted, + int StartFromIndex, int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC) +{ + ULootLockerServerAssetRequest::GetAssetsToGame(StartFromIndex, ItemsCount, AssetFilter, IncludeUGC, OnGetAssetsRequestCompleted, FServerAssetsResponseDelegate()); +} + +void ULootLockerServerManager::GetInventory(const FInventoryResponseBP& OnGetInventoryRequestCompleted, int PlayerId, int StartFromIndex, int ItemsCount) +{ + ULootLockerServerPlayerRequest::GetInventory(PlayerId, StartFromIndex, ItemsCount, OnGetInventoryRequestCompleted, FInventoryResponse()); +} + +void ULootLockerServerManager::AddAssetToPlayerInventory(const FAddAssetResponseBP& OnAddAssetRequestCompleted, + int32 PlayerId, FLootLockerServerAddAssetData AddAssetData) +{ + ULootLockerServerPlayerRequest::AddAssetToPlayerInventory(PlayerId, AddAssetData, OnAddAssetRequestCompleted, FAddAssetResponse()); +} + +void ULootLockerServerManager::GetPlayerLoadout(const FGetPlayerLoadoutResponseBP& OnGetPlayerLoadoutRequestCompleted, + int PlayerId) +{ + ULootLockerServerPlayerRequest::GetPlayerLoadout(PlayerId, OnGetPlayerLoadoutRequestCompleted, FGetPlayerLoadoutResponse()); +} + +void ULootLockerServerManager::EquipAssetForPlayerLoadout(const FEquipAssetResponseBP& OnRequestCompleted, int PlayerId, + int InstanceId) +{ + ULootLockerServerPlayerRequest::EquipAssetForPlayerLoadout(PlayerId, InstanceId, OnRequestCompleted, FEquipAssetResponse()); +} + +void ULootLockerServerManager::UnequipAssetForPlayerLoadout(const FUnequipAssetResponseBP& OnRequestCompleted, + int PlayerId, int LoadoutId) +{ + ULootLockerServerPlayerRequest::UnequipAssetForPlayerLoadout(PlayerId, LoadoutId, OnRequestCompleted, FUnequipAssetResponse()); +} + +void ULootLockerServerManager::GetPersistentStorage(const FGetPersistentStorageResponseBP& OnRequestCompleted, + TArray PlayerIds) +{ + ULootLockerServerStorageRequest::GetPersistentStorage(PlayerIds, OnRequestCompleted, FGetPersistentStorageResponse()); +} + +void ULootLockerServerManager::UpdatePersistentStorage(const FUpdatePersistentStorageResponseBP& OnCompletedRequest, + FLootLockerServerPersistentStorageRequestData RequestData) +{ + ULootLockerServerStorageRequest::UpdatePersistentStorage(RequestData, OnCompletedRequest, FUpdatePersistentStorageResponse()); +} + +void ULootLockerServerManager::GetPlayerCharacters(const FCharactersResponseBP& OnCompletedRequestBP, int PlayerId) +{ + ULootLockerServerCharacterRequest::GetPlayerCharacters(PlayerId, OnCompletedRequestBP, FCharactersResponse()); +} + +void ULootLockerServerManager::GetInventoryToCharacter(const FCharacterInventoryResponseBP& OnCompletedRequestBP, int PlayerId, + int CharacterId) +{ + ULootLockerServerCharacterRequest::GetInventoryToCharacter(PlayerId, CharacterId, OnCompletedRequestBP, FCharacterInventoryResponse()); +} + +void ULootLockerServerManager::GetCharacterLoadout(const FCharacterLoadoutResponseBP& OnCompletedRequestBP, + int PlayerId, int CharacterId) +{ + ULootLockerServerCharacterRequest::GetCharacterLoadout(PlayerId, CharacterId, OnCompletedRequestBP, FCharacterLoadoutResponse()); +} + +void ULootLockerServerManager::EquipAssetForCharacterLoadout(const FEquipResponseBP& OnCompletedRequestBP, int PlayerId, + int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::EquipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, OnCompletedRequestBP, FEquipResponse()); +} + +void ULootLockerServerManager::UnequipAssetForCharacterLoadout(const FUnequipResponseBP& OnCompletedRequestBP, + int PlayerId, int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::UnequipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, OnCompletedRequestBP, FUnequipResponse()); +} + +void ULootLockerServerManager::GetPlayerHeroes(const FHeroesResponseBP& OnCompletedRequestBP, int PlayerId) +{ + ULootLockerServerHeroesRequest::GetPlayerHeroes(PlayerId, OnCompletedRequestBP, FHeroesResponse()); +} + +void ULootLockerServerManager::GetInventoryToHero(const FHeroInventoryResponseBP& OnCompletedRequestBP, int PlayerId, + int HeroId) +{ + ULootLockerServerHeroesRequest::GetInventoryToHero(PlayerId, HeroId, OnCompletedRequestBP, FHeroInventoryResponse()); +} + +void ULootLockerServerManager::GetHeroLoadout(const FHeroLoadoutResponseBP& OnCompletedRequestBP, + int PlayerId, int HeroId) +{ + ULootLockerServerHeroesRequest::GetHeroLoadout(PlayerId, HeroId, OnCompletedRequestBP, FHeroLoadoutResponse()); +} + +void ULootLockerServerManager::EquipAssetForHeroLoadout(const FEquipHeroResponseBP& OnCompletedRequestBP, int PlayerId, + int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::EquipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, OnCompletedRequestBP, FEquipHeroResponse()); +} + +void ULootLockerServerManager::UnequipAssetForHeroLoadout(const FUnequipHeroResponseBP& OnCompletedRequestBP, + int PlayerId, int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::UnequipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, OnCompletedRequestBP, FUnequipHeroResponse()); +} + +void ULootLockerServerManager::InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponseBP& OnCompletedRequestBP, + FString Name, int PlayerId) +{ + ULootLockerServerTriggerRequest::InvokeTriggerOnBehalfOfPlayer(Name, PlayerId, OnCompletedRequestBP, FInvokeTriggerResponse()); +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp new file mode 100644 index 0000000..2a1aeac --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDK.cpp @@ -0,0 +1,42 @@ +// Copyright Epic Games, Inc. All Rights Reserved. +#include "LootLockerServerSDK.h" +#if WITH_EDITOR +#include "ISettingsModule.h" +#include "ISettingsSection.h" +#include "LootLockerServerConfig.h" +#endif +#include "UObject/ConstructorHelpers.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + + + +#define LOCTEXT_NAMESPACE "FLootLockerServerSDKModule" + +void FLootLockerServerSDKModule::StartupModule() +{ +#if WITH_EDITOR + + ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); + + if (SettingsModule != nullptr) + { + ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "Plugins", "LootLocker Server", + LOCTEXT("LootLockerSDKSettingsName", "LootLockerServerSDK"), + LOCTEXT("LootLockerSDKSettingsDescription", "Configure LootLockerServer SDK."), + GetMutableDefault() + ); + } + +#endif + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FLootLockerServerSDKModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FLootLockerServerSDKModule, LootLockerServerSDK) \ No newline at end of file diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp new file mode 100644 index 0000000..2b63570 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerServerSDKManager.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2021 LootLocker + + +#include "LootLockerServerSDKManager.h" + +//Authentication +void ULootLockerServerSDKManager::StartSession(const FServerAuthResponse& OnCompleteRequest) +{ + ULootLockerServerAuthRequest::StartSession( FServerAuthResponseBP(), OnCompleteRequest); +} + +void ULootLockerServerSDKManager::MaintainSession(const FServerAuthResponse& OnCompleteRequest) +{ + ULootLockerServerAuthRequest::MaintainSession(FServerAuthResponseBP(), OnCompleteRequest); +} + +void ULootLockerServerSDKManager::GetAssetsToGame(const FServerAssetsResponseDelegate& OnCompletedRequest, int StartFromIndex, + int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC) +{ + ULootLockerServerAssetRequest::GetAssetsToGame(StartFromIndex, ItemsCount, AssetFilter, IncludeUGC, FServerAssetsResponseDelegateBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetInventory(const FInventoryResponse& OnGetInventoryRequestCompleted, + int PlayerId, int StartFromIndex, int ItemsCount) +{ + ULootLockerServerPlayerRequest::GetInventory(PlayerId, StartFromIndex, ItemsCount, FInventoryResponseBP(), OnGetInventoryRequestCompleted); +} + +void ULootLockerServerSDKManager::AddAssetToPlayerInventory(const FAddAssetResponse& OnAddAssetRequestCompleted, + int PlayerId, FLootLockerServerAddAssetData AddAssetData) +{ + ULootLockerServerPlayerRequest::AddAssetToPlayerInventory(PlayerId, AddAssetData, FAddAssetResponseBP(), OnAddAssetRequestCompleted); +} + +void ULootLockerServerSDKManager::GetPlayerLoadout(const FGetPlayerLoadoutResponse& OnGetPlayerLoadoutRequestCompleted, + int PlayerId) +{ + ULootLockerServerPlayerRequest::GetPlayerLoadout(PlayerId, FGetPlayerLoadoutResponseBP(), OnGetPlayerLoadoutRequestCompleted); +} + +void ULootLockerServerSDKManager::EquipAssetForPlayerLoadout(const FEquipAssetResponse& OnRequestCompleted, + int PlayerId, int InstanceId) +{ + ULootLockerServerPlayerRequest::EquipAssetForPlayerLoadout(PlayerId, InstanceId, FEquipAssetResponseBP(), OnRequestCompleted); +} + +void ULootLockerServerSDKManager::UnequipAssetForPlayerLoadout(const FUnequipAssetResponse& OnRequestCompleted, + int PlayerId, int LoadoutId) +{ + ULootLockerServerPlayerRequest::UnequipAssetForPlayerLoadout(PlayerId, LoadoutId, FUnequipAssetResponseBP(), OnRequestCompleted); +} + +void ULootLockerServerSDKManager::GetPersistentStorage(const FGetPersistentStorageResponse& OnRequestCompleted, + TArray PlayerIds) +{ + ULootLockerServerStorageRequest::GetPersistentStorage(PlayerIds, FGetPersistentStorageResponseBP(), OnRequestCompleted); +} + +void ULootLockerServerSDKManager::UpdatePersistentStorage(const FUpdatePersistentStorageResponse& OnCompletedRequest, + FLootLockerServerPersistentStorageRequestData RequestData) +{ + ULootLockerServerStorageRequest::UpdatePersistentStorage(RequestData, FUpdatePersistentStorageResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetPlayerCharacters(const FCharactersResponse& OnCompletedRequest, int PlayerId) +{ + ULootLockerServerCharacterRequest::GetPlayerCharacters(PlayerId, FCharactersResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetInventoryToCharacter(const FCharacterInventoryResponse& OnCompletedRequest, + int PlayerId, int CharacterId) +{ + ULootLockerServerCharacterRequest::GetInventoryToCharacter(PlayerId, CharacterId, FCharacterInventoryResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetCharacterLoadout(const FCharacterLoadoutResponse& OnCompletedRequest, int PlayerId, + int CharacterId) +{ + ULootLockerServerCharacterRequest::GetCharacterLoadout(PlayerId, CharacterId, FCharacterLoadoutResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::EquipAssetForCharacterLoadout(const FEquipResponse& OnCompletedRequest, + int PlayerId, int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::EquipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, FEquipResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::UnequipAssetForCharacterLoadout(const FUnequipResponse& OnCompletedRequest, + int PlayerId, int CharacterId, int InstanceId) +{ + ULootLockerServerCharacterRequest::UnequipAssetForCharacterLoadout(PlayerId, CharacterId, InstanceId, FUnequipResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetPlayerHeroes(const FHeroesResponse& OnCompletedRequest, int PlayerId) +{ + ULootLockerServerHeroesRequest::GetPlayerHeroes(PlayerId, FHeroesResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetInventoryToHero(const FHeroInventoryResponse& OnCompletedRequest, + int PlayerId, int HeroId) +{ + ULootLockerServerHeroesRequest::GetInventoryToHero(PlayerId, HeroId, FHeroInventoryResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::GetHeroLoadout(const FHeroLoadoutResponse& OnCompletedRequest, int PlayerId, + int HeroId) +{ + ULootLockerServerHeroesRequest::GetHeroLoadout(PlayerId, HeroId, FHeroLoadoutResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::EquipAssetForHeroLoadout(const FEquipHeroResponse& OnCompletedRequest, + int PlayerId, int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::EquipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, FEquipHeroResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::UnequipAssetForHeroLoadout(const FUnequipHeroResponse& OnCompletedRequest, + int PlayerId, int HeroId, int InstanceId) +{ + ULootLockerServerHeroesRequest::UnequipAssetForHeroLoadout(PlayerId, HeroId, InstanceId, FUnequipHeroResponseBP(), OnCompletedRequest); +} + +void ULootLockerServerSDKManager::InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponse& OnCompletedRequest, + FString Name, int PlayerId) +{ + ULootLockerServerTriggerRequest::InvokeTriggerOnBehalfOfPlayer(Name, PlayerId, FInvokeTriggerResponseBP(), OnCompletedRequest); +} + + diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp new file mode 100644 index 0000000..c86824a --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/LootLockerSrvPersitentDataHolder.cpp @@ -0,0 +1,13 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "LootLockerSrvPersitentDataHolder.h" + +FString ULootLockerSrvPersitentDataHolder::Token = ""; +FString ULootLockerSrvPersitentDataHolder::CachedLastEndpointUsed = ""; +FString ULootLockerSrvPersitentDataHolder::CachedLastRequestTypeUsed = ""; +FString ULootLockerSrvPersitentDataHolder::CachedLastDataSentToServer = ""; +FString ULootLockerSrvPersitentDataHolder::CachedSteamToken = ""; +FString ULootLockerSrvPersitentDataHolder::CachedPlayerIdentifier = ""; +FString ULootLockerSrvPersitentDataHolder::AdminToken = ""; +FString ULootLockerSrvPersitentDataHolder::ServerToken = ""; \ No newline at end of file diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp new file mode 100644 index 0000000..c20faad --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAssetRequest.cpp @@ -0,0 +1,81 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerAssetRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" +#include "LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h" + +ULootLockerServerHttpClient* ULootLockerServerAssetRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerAssetRequest::ULootLockerServerAssetRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerAssetRequest::GetAssetsToGame(int StartFromIndex, int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC, const FServerAssetsResponseDelegateBP& OnCompletedRequestBP, const FServerAssetsResponseDelegate& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetAssetsToGameResponse ResponseStruct; + if (response.success) + { + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + ResponseStruct.success = true; + } + else { + ResponseStruct.success = false; + UE_LOG(LogTemp, Error, TEXT("GetAssets failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetAssetsToGameEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + FString EndpointUrl = Endpoint.endpoint; + if (StartFromIndex != 0) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "after=" + FString::FromInt(StartFromIndex)); + } + if (ItemsCount != 50) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "count=" + FString::FromInt(ItemsCount)); + } + FString Filter = ""; + switch (AssetFilter) + { + case ELootLockerServerAssetFilter::Purchasable: + Filter = "purchasable"; + break; + case ELootLockerServerAssetFilter::NonPurchasable: + Filter = "!purchasable"; + break; + case ELootLockerServerAssetFilter::Rentable: + Filter = "rentable"; + break; + case ELootLockerServerAssetFilter::NonRentable: + Filter = "!rentable"; + break; + case ELootLockerServerAssetFilter::Popular: + Filter = "popular"; + break; + case ELootLockerServerAssetFilter::UnPopular: + Filter = "!popular"; + break; + } + if (!Filter.IsEmpty()) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "filter=" + Filter); + } + + if (IncludeUGC) + { + EndpointUrl = LootLockerServerUtilities::AppendParameterToUrl(EndpointUrl, "include_ugc=true"); + } + HttpClient->SendApi(EndpointUrl, requestMethod, ContentString, sessionResponse, true); +} + diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp new file mode 100644 index 0000000..2ba419d --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerAuthRequest.cpp @@ -0,0 +1,79 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerAuthRequest.h" +#include "LootLockerServerGameEndpoints.h" +#include "LootLockerSrvPersitentDataHolder.h" + + +ULootLockerServerHttpClient* ULootLockerServerAuthRequest::HttpClient = nullptr; + +ULootLockerServerAuthRequest::ULootLockerServerAuthRequest() +{ + HttpClient = NewObject(); +} +// +void ULootLockerServerAuthRequest::StartSession(const FServerAuthResponseBP& OnCompletedRequestBP, const FServerAuthResponse& OnCompletedRequest) +{ + FLootLockerServerAuthenticationRequest authRequest; + const ULootLockerServerConfig* config = GetDefault(); + authRequest.development_mode = config->OnDevelopmentMode; + authRequest.game_version = config->GameVersion; + FString AuthContentString; + FJsonObjectConverter::UStructToJsonObjectString(FLootLockerServerAuthenticationRequest::StaticStruct(), &authRequest, AuthContentString, 0, 0); + + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest, config](FLootLockerServerResponse response) + { + FLootLockerServerAuthenticationResponse ResponseStruct; + if (response.success) + { + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + ULootLockerSrvPersitentDataHolder::ServerToken = ResponseStruct.token; + ResponseStruct.success = true; + } + else + { + ResponseStruct.success = false; + UE_LOG(LogTemp, Error, TEXT("Starting of Session failed")); + } + + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints endpoint = ULootLockerServerGameEndpoints::StartSessionEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(endpoint.requestMethod)); + HttpClient->SendApi(endpoint.endpoint, requestMethod, AuthContentString, sessionResponse); +} + +void ULootLockerServerAuthRequest::MaintainSession(const FServerAuthResponseBP& OnCompletedRequestBP, const FServerAuthResponse& OnCompletedRequest) +{ + FLootLockerServerAuthenticationRequest authRequest; + const ULootLockerServerConfig* config = GetDefault(); + authRequest.development_mode = config->OnDevelopmentMode; + authRequest.game_version = config->GameVersion; + FString AuthContentString; + FJsonObjectConverter::UStructToJsonObjectString(FLootLockerServerAuthenticationRequest::StaticStruct(), &authRequest, AuthContentString, 0, 0); + + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest, config](FLootLockerServerResponse response) + { + FLootLockerServerAuthenticationResponse ResponseStruct; + if (response.success) + { + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + ResponseStruct.success = true; + } + else + { + ResponseStruct.success = false; + UE_LOG(LogTemp, Error, TEXT("Starting of Session failed")); + } + + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints endpoint = ULootLockerServerGameEndpoints::StartSessionEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(endpoint.requestMethod)); + HttpClient->SendApi(endpoint.endpoint, requestMethod, AuthContentString, sessionResponse,true); +} \ No newline at end of file diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp new file mode 100644 index 0000000..e66c21f --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerCharacterRequest.cpp @@ -0,0 +1,162 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerCharacterRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerCharacterRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerCharacterRequest::ULootLockerServerCharacterRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerCharacterRequest::GetPlayerCharacters(int PlayerId, + const FCharactersResponseBP& OnCompletedRequestBP, const FCharactersResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPlayerCharactersResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerCharactersEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::GetInventoryToCharacter(int PlayerId, int CharacterId, + const FCharacterInventoryResponseBP& OnCompletedRequestBP, const FCharacterInventoryResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerCharacterInventoryResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetInventorytoCharacterEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::GetCharacterLoadout(int PlayerId, int CharacterId, + const FCharacterLoadoutResponseBP& OnCompletedRequestBP, const FCharacterLoadoutResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetCharacterLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetCharacterLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::EquipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, + const FEquipResponseBP& OnCompletedRequestBP, const FEquipResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerEquipCharacterResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("instance_id")), FString::FromInt(InstanceId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::EquipAssetforCharacterLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerCharacterRequest::UnequipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, + const FUnequipResponseBP& OnCompletedRequestBP, const FUnequipResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUnequipCharacterResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UnequipAssetforCharacterLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, CharacterId, InstanceId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp new file mode 100644 index 0000000..d198f5f --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerHeroesRequest.cpp @@ -0,0 +1,161 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerHeroesRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerHeroesRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerHeroesRequest::ULootLockerServerHeroesRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerHeroesRequest::GetPlayerHeroes(int PlayerId, + const FHeroesResponseBP& OnCompletedRequestBP, const FHeroesResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPlayerHeroesResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerHeroesEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::GetInventoryToHero(int PlayerId, int HeroId, + const FHeroInventoryResponseBP& OnCompletedRequestBP, const FHeroInventoryResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerHeroInventoryResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetInventorytoHeroEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::GetHeroLoadout(int PlayerId, int HeroId, + const FHeroLoadoutResponseBP& OnCompletedRequestBP, const FHeroLoadoutResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetHeroLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetHeroLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::EquipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, + const FEquipHeroResponseBP& OnCompletedRequestBP, const FEquipHeroResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerEquipHeroResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("instance_id")), FString::FromInt(InstanceId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::EquipAssetforHeroLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerHeroesRequest::UnequipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, + const FUnequipHeroResponseBP& OnCompletedRequestBP, const FUnequipHeroResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUnequipHeroResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UnequipAssetforHeroLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, HeroId, InstanceId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} \ No newline at end of file diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp new file mode 100644 index 0000000..9528cdd --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerPlayerRequest.cpp @@ -0,0 +1,168 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerPlayerRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerPlayerRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerPlayerRequest::ULootLockerServerPlayerRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerPlayerRequest::GetInventory(int PlayerId, int StartFromIndex, int ItemsCount, + const FInventoryResponseBP& OnCompletedRequestBP, const FInventoryResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerInventoryResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerInventoryEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::AddAssetToPlayerInventory(int PlayerId, FLootLockerServerAddAssetData AddAssetData, + const FAddAssetResponseBP& OnCompletedRequestBP, const FAddAssetResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerAddAssetResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + FJsonObjectConverter::UStructToJsonObject(FLootLockerServerAddAssetData::StaticStruct(), &AddAssetData, ItemJson, 0, 0); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::AddAssetsToPlayerInventoryEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::GetPlayerLoadout(int PlayerId, + const FGetPlayerLoadoutResponseBP& OnCompletedRequestBP, const FGetPlayerLoadoutResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPlayerLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPlayerLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::EquipAssetForPlayerLoadout(int PlayerId, int InstanceId, + const FEquipAssetResponseBP& OnCompletedRequestBP, const FEquipAssetResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerEquipAssetForPlayerLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("instance_id")), FString::FromInt(InstanceId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::EquipAssetToPlayerLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + UE_LOG(LogTemp, Log, TEXT("data=%s"), *ContentString); + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} + +void ULootLockerServerPlayerRequest::UnequipAssetForPlayerLoadout(int PlayerId, int LoadoutId, + const FUnequipAssetResponseBP& OnCompletedRequestBP, const FUnequipAssetResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUnequipAssetForPlayerLoadoutResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting player failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + FString ContentString; + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UnEquipAssetToPlayerLoadoutEndpoint; + FString endpoint = FString::Format(*(Endpoint.endpoint), { PlayerId, LoadoutId }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp new file mode 100644 index 0000000..e5370c5 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerStorageRequest.cpp @@ -0,0 +1,81 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerStorageRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerStorageRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerStorageRequest::ULootLockerServerStorageRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerStorageRequest::GetPersistentStorage(TArray PlayerIds, + const FGetPersistentStorageResponseBP& OnCompletedRequestBP, + const FGetPersistentStorageResponse& OnCompletedRequest) +{ + FString data; + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerGetPersistentStorageResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Getting players failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::GetPersistentStorageEndpoint; + TArray PlayerStringIds; + for (int Id : PlayerIds) + { + PlayerStringIds.Add(FString::FromInt(Id)); + } + + FString endpoint = FString::Format(*(Endpoint.endpoint), { FString::Join(PlayerStringIds, TEXT(",")) }); + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + HttpClient->SendApi(endpoint, requestMethod, data, sessionResponse, true); +} + +void ULootLockerServerStorageRequest::UpdatePersistentStorage(FLootLockerServerPersistentStorageRequestData requestData, + const FUpdatePersistentStorageResponseBP& OnCompletedRequestBP, + const FUpdatePersistentStorageResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerUpdatePersistentStorageResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Failed to update persisten storage")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + FJsonObjectConverter::UStructToJsonObject(FLootLockerServerPersistentStorageRequestData::StaticStruct(), &requestData, ItemJson, 0, 0); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::UpdatePersistentStorageEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(Endpoint.endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp new file mode 100644 index 0000000..b6d45e8 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/ServerAPI/LootLockerServerTriggerRequest.cpp @@ -0,0 +1,47 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "ServerAPI/LootLockerServerTriggerRequest.h" + +#include "JsonObjectConverter.h" +#include "LootLockerServerGameEndpoints.h" + +ULootLockerServerHttpClient* ULootLockerServerTriggerRequest::HttpClient = nullptr; +// Sets default values for this component's properties +ULootLockerServerTriggerRequest::ULootLockerServerTriggerRequest() +{ + HttpClient = NewObject(); +} + +void ULootLockerServerTriggerRequest::InvokeTriggerOnBehalfOfPlayer(FString name, int PlayerId, + const FInvokeTriggerResponseBP& OnCompletedRequestBP, const FInvokeTriggerResponse& OnCompletedRequest) +{ + FServerResponseCallback sessionResponse = FServerResponseCallback::CreateLambda([OnCompletedRequestBP, OnCompletedRequest](FLootLockerServerResponse response) + { + FLootLockerServerInvokeTriggerResponse ResponseStruct; + if (response.success) + { + response.success = true; + FJsonObjectConverter::JsonObjectStringToUStruct(response.FullTextFromServer, &ResponseStruct, 0, 0); + + } + else { + response.success = false; + UE_LOG(LogTemp, Error, TEXT("Invoking trigger failed from lootlocker")); + } + ResponseStruct.FullTextFromServer = response.FullTextFromServer; + OnCompletedRequestBP.ExecuteIfBound(ResponseStruct); + OnCompletedRequest.ExecuteIfBound(ResponseStruct); + }); + + TSharedRef ItemJson = MakeShareable(new FJsonObject()); + ItemJson->SetStringField(FString(TEXT("name")), name); + ItemJson->SetStringField(FString(TEXT("player_id")), FString::FromInt(PlayerId)); + FString ContentString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&ContentString); + FJsonSerializer::Serialize(ItemJson, Writer); + FLootLockerServerEndPoints Endpoint = ULootLockerServerGameEndpoints::InvokeTriggeronBehalfofPlayerEndpoint; + FString requestMethod = ULootLockerServerConfig::GetEnum(TEXT("ELootLockerServerHTTPMethod"), static_cast(Endpoint.requestMethod)); + + HttpClient->SendApi(Endpoint.endpoint, requestMethod, ContentString, sessionResponse, true); +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp new file mode 100644 index 0000000..9e614ad --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2021 LootLocker + +#include "LootLockerServerUtilities.h" + +namespace LootLockerServerUtilities +{ + FString AppendParameterToUrl(const FString& Url, const FString& Parameter) + { + FString AppendSymbol = Url.Contains("?") ? "&" : "?"; + return Url + AppendSymbol + Parameter; + } +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h new file mode 100644 index 0000000..c8f3a36 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Private/Utils/LootLockerServerUtilities.h @@ -0,0 +1,8 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +namespace LootLockerServerUtilities +{ + FString AppendParameterToUrl(const FString& Url, const FString& Parameter); +} diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h new file mode 100644 index 0000000..65c8f5c --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerConfig.h @@ -0,0 +1,69 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "HttpModule.h" +#include "LootLockerServerConfig.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerResponse +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool success; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + bool ServerCallHasError; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + int ServerCallStatusCode; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString FullTextFromServer; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString ServerError; +}; + +UENUM(BlueprintType) +enum class ELootLockerServerHTTPMethod : uint8 +{ + GET = 0 UMETA(DisplayName = "GET"), + POST = 1 UMETA(DisplayName = "POST"), + DELETE = 2 UMETA(DisplayName = "DELETE"), + PUT = 3 UMETA(DisplayName = "PUT"), + HEAD = 4 UMETA(DisplayName = "HEAD"), + CREATE = 5 UMETA(DisplayName = "CREATE"), + OPTIONS = 6 UMETA(DisplayName = "OPTIONS"), + PATCH = 7 UMETA(DisplayName = "PATCH"), + UPLOAD = 8 UMETA(DisplayName = "UPLOAD") +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerEndPoints +{ + GENERATED_BODY() +public: + UPROPERTY(BlueprintReadWrite, EditAnywhere) + FString endpoint; + UPROPERTY(BlueprintReadWrite, EditAnywhere) + ELootLockerServerHTTPMethod requestMethod; +}; + +DECLARE_DELEGATE_OneParam(FServerResponseCallback, FLootLockerServerResponse); + +UCLASS(Config = LootLockerServerSDK) +class LOOTLOCKERSERVERSDK_API ULootLockerServerConfig : public UObject +{ + GENERATED_BODY() +public: + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + FString LootLockerServerKey; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + bool OnDevelopmentMode; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + FString GameVersion; + UPROPERTY(Config, EditAnywhere, BlueprintReadWrite, Category = "LootLocker") + FString LootLockerVersion; + + static FString GetEnum(const TCHAR* Enum, int32 EnumValue); +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h new file mode 100644 index 0000000..7ad659b --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerGameEndpoints.h @@ -0,0 +1,46 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "LootLockerServerConfig.h" + +#include "LootLockerServerGameEndpoints.generated.h" + +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerGameEndpoints : public UObject +{ + GENERATED_BODY() +public: + //Auth + static FLootLockerServerEndPoints StartSessionEndpoint; + static FLootLockerServerEndPoints MaintainingSessionEndpoint; + //Assets + static FLootLockerServerEndPoints GetAssetsToGameEndpoint; + //Player Inventory + static FLootLockerServerEndPoints GetPlayerInventoryEndpoint; + static FLootLockerServerEndPoints AddAssetsToPlayerInventoryEndpoint; + static FLootLockerServerEndPoints GetPlayerLoadoutEndpoint; + static FLootLockerServerEndPoints EquipAssetToPlayerLoadoutEndpoint; + static FLootLockerServerEndPoints UnEquipAssetToPlayerLoadoutEndpoint; + //Player persisitent storage + static FLootLockerServerEndPoints GetPersistentStorageEndpoint; + static FLootLockerServerEndPoints UpdatePersistentStorageEndpoint; + //Characters & Heroes + static FLootLockerServerEndPoints GetPlayerCharactersEndpoint; + static FLootLockerServerEndPoints GetInventorytoCharacterEndpoint; + static FLootLockerServerEndPoints GetCharacterLoadoutEndpoint; + static FLootLockerServerEndPoints EquipAssetforCharacterLoadoutEndpoint; + static FLootLockerServerEndPoints UnequipAssetforCharacterLoadoutEndpoint; + static FLootLockerServerEndPoints GetPlayerHeroesEndpoint; + static FLootLockerServerEndPoints GetInventorytoHeroEndpoint; + static FLootLockerServerEndPoints GetHeroLoadoutEndpoint; + static FLootLockerServerEndPoints EquipAssetforHeroLoadoutEndpoint; + static FLootLockerServerEndPoints UnequipAssetforHeroLoadoutEndpoint; + //Trigger + static FLootLockerServerEndPoints InvokeTriggeronBehalfofPlayerEndpoint; + +private: + static FString GameBaseUrl; + + static FLootLockerServerEndPoints InitEndpoint(const FString& Endpoint, ELootLockerServerHTTPMethod Method); +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h new file mode 100644 index 0000000..be116f9 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerHttpClient.h @@ -0,0 +1,26 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "Interfaces/IHttpRequest.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.generated.h" + +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerHttpClient : public UObject +{ + GENERATED_BODY() + +public: + ULootLockerServerHttpClient(); + void SendApi(const FString& endPoint, const FString& requestType, const FString& data, const FServerResponseCallback& onCompleteRequest, bool useHeader = false); + void UploadFile(const FString& endPoint, const FString& requestType, const FString& FilePath, const TMap AdditionalFields, const FServerResponseCallback& onCompleteRequest, bool useHeader = false, bool useAdmin = false); +public: + bool ResponseIsValid(const FHttpResponsePtr& InResponse, bool bWasSuccessful); + FServerResponseCallback savedOnCompleteRequest; +}; + + + diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h new file mode 100644 index 0000000..228897b --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerManager.h @@ -0,0 +1,177 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "ServerAPI/LootLockerServerAssetRequest.h" +#include "ServerAPI/LootLockerServerAuthRequest.h" +#include "ServerAPI/LootLockerServerCharacterRequest.h" +#include "ServerAPI/LootLockerServerHeroesRequest.h" +#include "ServerAPI/LootLockerServerPlayerRequest.h" +#include "ServerAPI/LootLockerServerStorageRequest.h" +#include "ServerAPI/LootLockerServerTriggerRequest.h" + + + +#include "LootLockerServerManager.generated.h" + +UCLASS(Blueprintable) +class LOOTLOCKERSERVERSDK_API ULootLockerServerManager : public UObject +{ + GENERATED_BODY() + +public: + + //================================================== + //Authentication + //================================================== + + /** + * Register a session. + * + * @param PlayerId - the ID of the player on the platform the game is currently running on. + * https://docs.lootlocker.io/game-api/#authentication-request + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Authentication") + static void StartSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted); + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Authentication") + static void MaintainSession(const FServerAuthResponseBP& OnStartedSessionRequestCompleted); + + /** + * Get all assets in a paginated form. + * + * @param StartFromIndex - index of the item to start from. + * @param ItemsCount - number of items to receive (50-200). + * @param AssetFilter - optional filter. + * @param IncludeUGC - whether to include UGC Assets. + * https://docs.lootlocker.io/server-api/#get-assets-to-game + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Assets") + static void GetAssetsToGame(const FServerAssetsResponseDelegateBP& OnGetAssetsRequestCompleted,int StartFromIndex = 0, int ItemsCount = 50, ELootLockerServerAssetFilter AssetFilter = ELootLockerServerAssetFilter::None, bool IncludeUGC = false); + + /** + * Get a paginated list of the players inventory. + * https://docs.lootlocker.io/server-api/#get-player-inventory + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetInventory(const FInventoryResponseBP& OnGetInventoryRequestCompleted, int PlayerId, int StartFromIndex = 0, int ItemsCount = 50); + + /** + * Grant an asset to a player as you see fit + * https://docs.lootlocker.io/server-api/#add-asset-to-player-inventory + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void AddAssetToPlayerInventory(const FAddAssetResponseBP& OnAddAssetRequestCompleted, int32 PlayerId, FLootLockerServerAddAssetData AddAssetData); + + /** + * Return the players default characters loadout + * https://docs.lootlocker.io/server-api/#get-player-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPlayerLoadout(const FGetPlayerLoadoutResponseBP& OnGetPlayerLoadoutRequestCompleted, int PlayerId); + + /** + * Equip an asset instance to the players default character + * https://docs.lootlocker.io/server-api/#equip-asset-for-player-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void EquipAssetForPlayerLoadout(const FEquipAssetResponseBP& OnRequestCompleted, int PlayerId, int InstanceId); + + /** + * Unequip an asset instance for the players default character + * https://docs.lootlocker.io/server-api/#unequip-asset-for-player-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UnequipAssetForPlayerLoadout(const FUnequipAssetResponseBP& OnRequestCompleted, int PlayerId, int LoadoutId); + + /** + * Read player storage one or more players + * https://docs.lootlocker.io/server-api/#player-persistent-storage + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPersistentStorage(const FGetPersistentStorageResponseBP& OnRequestCompleted, TArray PlayerIds); + + /** + * Update Persistent Storage + * https://docs.lootlocker.io/server-api/#update-persistent-storage + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UpdatePersistentStorage(const FUpdatePersistentStorageResponseBP& OnCompletedRequest, FLootLockerServerPersistentStorageRequestData RequestData); + + /** + * List characters to a player + * https://docs.lootlocker.io/server-api/#get-player-characters + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPlayerCharacters(const FCharactersResponseBP& OnCompletedRequestBP, int PlayerId); + + /** + * Get the inventory for a specific character belonging to a player + * https://docs.lootlocker.io/server-api/#get-inventory-to-character + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetInventoryToCharacter(const FCharacterInventoryResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId); + + /** + * Get a characters full loadout. + * https://docs.lootlocker.io/server-api/#get-character-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetCharacterLoadout(const FCharacterLoadoutResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId); + + /** + * Equip an asset instance to a specific character + * https://docs.lootlocker.io/server-api/#equip-asset-for-character-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void EquipAssetForCharacterLoadout(const FEquipResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId, int InstanceId); + + /** + * Unequip an asset instance for a character + * https://docs.lootlocker.io/server-api/#unequip-asset-for-character-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UnequipAssetForCharacterLoadout(const FUnequipResponseBP& OnCompletedRequestBP, int PlayerId, int CharacterId, int InstanceId); + + /** + * List heroes to a player + * https://docs.lootlocker.io/server-api/#get-player-heroes + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetPlayerHeroes(const FHeroesResponseBP& OnCompletedRequestBP, int PlayerId); + + /** + * Get the inventory for a specific hero belonging to a player + * https://docs.lootlocker.io/server-api/#get-inventory-to-hero + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetInventoryToHero(const FHeroInventoryResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId); + + /** + * Get a hero full loadout. + * https://docs.lootlocker.io/server-api/#get-hero-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void GetHeroLoadout(const FHeroLoadoutResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId); + + /** + * Equip an asset instance to a specific hero + * https://docs.lootlocker.io/server-api/#equip-asset-for-hero-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void EquipAssetForHeroLoadout(const FEquipHeroResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId, int InstanceId); + + /** + * Unequip an asset instance for a hero + * https://docs.lootlocker.io/server-api/#unequip-asset-for-hero-loadout + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void UnequipAssetForHeroLoadout(const FUnequipHeroResponseBP& OnCompletedRequestBP, int PlayerId, int HeroId, int InstanceId); + + /** + * Invoke a trigger on behalf of a player + * https://docs.lootlocker.io/server-api/#invoke-trigger-on-behalf-of-player + */ + UFUNCTION(BlueprintCallable, Category = "LootLockerServer Methods | Players") + static void InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponseBP& OnCompletedRequestBP, FString Name, int PlayerId); +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h new file mode 100644 index 0000000..20f34df --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDK.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FLootLockerServerSDKModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h new file mode 100644 index 0000000..8b024a3 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerServerSDKManager.h @@ -0,0 +1,211 @@ +// Copyright (c) 2021 LootLocker + +#pragma once + +#include "CoreMinimal.h" +#include "ServerAPI/LootLockerServerAssetRequest.h" +#include "ServerAPI/LootLockerServerAuthRequest.h" +#include "ServerAPI/LootLockerServerCharacterRequest.h" +#include "ServerAPI/LootLockerServerHeroesRequest.h" +#include "ServerAPI/LootLockerServerPlayerRequest.h" +#include "ServerAPI/LootLockerServerStorageRequest.h" +#include "ServerAPI/LootLockerServerTriggerRequest.h" + + + +#include "LootLockerServerSDKManager.generated.h" + +UCLASS(Blueprintable) +class LOOTLOCKERSERVERSDK_API ULootLockerServerSDKManager : public UObject +{ + GENERATED_BODY() + +public: + + //================================================== + //Authentication + //================================================== + + /** + * Register a session. + * @param PlayerId - the ID of the player on the platform the game is currently running on. + * @param OnCompletedRequest - callback to be invoked with the server response. + * https://docs.lootlocker.io/game-api/#authentication-request + */ + static void StartSession(const FServerAuthResponse& OnCompletedRequest); + + static void MaintainSession(const FServerAuthResponse& OnCompletedRequest); + + /** + * Get all assets in a paginated form. + * + * @param OnCompletedRequest - callback to be invoked with the server response. + * @param StartFromIndex - index of the item to start from. + * @param ItemsCount - number of items to receive (50-200). + * @param AssetFilter - optional filter. + * @param IncludeUGC - whether to include UGC Assets. + * https://docs.lootlocker.io/game-api/#getting-asset-list + */ + static void GetAssetsToGame(const FServerAssetsResponseDelegate& OnCompletedRequest, int StartFromIndex = 0, int ItemsCount = 50, ELootLockerServerAssetFilter AssetFilter = ELootLockerServerAssetFilter::None, bool IncludeUGC = false); + + /** + * Get a paginated list of the players inventory. + * @param OnGetInventoryRequestCompleted - callback to be invoked with the server response + * @param StartFromIndex - index of the item to start from + * @param ItemsCount - number of items to receive (50-200) + * https://docs.lootlocker.io/server-api/#get-player-inventory + */ + static void GetInventory(const FInventoryResponse& OnGetInventoryRequestCompleted, int PlayerId, int StartFromIndex = 0, int ItemsCount = 50); + + /** + * Grant an asset to a player as you see fit + * @param OnAddAssetRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * @param AddAssetData - Data about the asset to be added + * https://docs.lootlocker.io/server-api/#add-asset-to-player-inventory + */ + static void AddAssetToPlayerInventory(const FAddAssetResponse& OnAddAssetRequestCompleted, int PlayerId, FLootLockerServerAddAssetData AddAssetData); + + /** + * Return the players default characters loadout + * @param OnGetPlayerLoadoutRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * https://docs.lootlocker.io/server-api/#get-player-loadout + */ + static void GetPlayerLoadout(const FGetPlayerLoadoutResponse& OnGetPlayerLoadoutRequestCompleted, int PlayerId); + + /** + * Equip an asset instance to the players default character + * @param OnRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * @param InstanceId - Asset id + * https://docs.lootlocker.io/server-api/#equip-asset-for-player-loadout + */ + static void EquipAssetForPlayerLoadout(const FEquipAssetResponse& OnRequestCompleted, int PlayerId, int InstanceId); + + /** + * Unequip an asset instance for the players default character + * @param OnRequestCompleted - callback to be invoked with the server response + * @param PlayerId - Player identifier + * @param LoadoutId - Asset id + * https://docs.lootlocker.io/server-api/#equip-asset-for-player-loadout + */ + static void UnequipAssetForPlayerLoadout(const FUnequipAssetResponse& OnRequestCompleted, int PlayerId, int LoadoutId); + + /** + * Read player storage one or more players + * @param OnRequestCompleted - callback to be invoked with the server response + * @param PlayerIds - array of player ids + * https://docs.lootlocker.io/server-api/#player-persistent-storage + */ + static void GetPersistentStorage(const FGetPersistentStorageResponse& OnRequestCompleted, TArray PlayerIds); + + /** + * Update Persistent Storage + * @param OnCompletedRequest - callback to be invoked with the server response + * @param RequestData - payload + * https://docs.lootlocker.io/server-api/#update-persistent-storage + */ + static void UpdatePersistentStorage(const FUpdatePersistentStorageResponse& OnCompletedRequest, FLootLockerServerPersistentStorageRequestData RequestData); + + /** + * List characters to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * https://docs.lootlocker.io/server-api/#get-player-characters + */ + static void GetPlayerCharacters(const FCharactersResponse& OnCompletedRequest, int PlayerId); + + /** + * Get the inventory for a specific character belonging to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * https://docs.lootlocker.io/server-api/#get-inventory-to-character + */ + static void GetInventoryToCharacter(const FCharacterInventoryResponse& OnCompletedRequest, int PlayerId, int CharacterId); + + /** + * Get a characters full loadout. + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * https://docs.lootlocker.io/server-api/#get-character-loadout + */ + static void GetCharacterLoadout(const FCharacterLoadoutResponse& OnCompletedRequest, int PlayerId, int CharacterId); + + /** + * Equip an asset instance to a specific character + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#equip-asset-for-character-loadout + */ + static void EquipAssetForCharacterLoadout(const FEquipResponse& OnCompletedRequest, int PlayerId, int CharacterId, int InstanceId); + + /** + * Unequip an asset instance for a character + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param CharacterId - character identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#unequip-asset-for-character-loadout + */ + static void UnequipAssetForCharacterLoadout(const FUnequipResponse& OnCompletedRequest, int PlayerId, int CharacterId, int InstanceId); + + /** + * List heroes to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * https://docs.lootlocker.io/server-api/#get-player-heroes + */ + static void GetPlayerHeroes(const FHeroesResponse& OnCompletedRequest, int PlayerId); + + /** + * Get the inventory for a specific hero belonging to a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * https://docs.lootlocker.io/server-api/#get-inventory-to-hero + */ + static void GetInventoryToHero(const FHeroInventoryResponse& OnCompletedRequest, int PlayerId, int HeroId); + + /** + * Get a heroes full loadout. + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * https://docs.lootlocker.io/server-api/#get-hero-loadout + */ + static void GetHeroLoadout(const FHeroLoadoutResponse& OnCompletedRequest, int PlayerId, int HeroId); + + /** + * Equip an asset instance to a specific hero + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#equip-asset-for-hero-loadout + */ + static void EquipAssetForHeroLoadout(const FEquipHeroResponse& OnCompletedRequest, int PlayerId, int HeroId, int InstanceId); + + /** + * Unequip an asset instance for a hero + * @param OnCompletedRequest - callback to be invoked with the server response + * @param PlayerId - player identifier + * @param HeroId - hero identifier + * @param InstanceId - instance identifier + * https://docs.lootlocker.io/server-api/#unequip-asset-for-hero-loadout + */ + static void UnequipAssetForHeroLoadout(const FUnequipHeroResponse& OnCompletedRequest, int PlayerId, int HeroId, int InstanceId); + + /** + * Invoke a trigger on behalf of a player + * @param OnCompletedRequest - callback to be invoked with the server response + * @param Name - trigger name + * @param PlayerId - player identifier + * https://docs.lootlocker.io/server-api/#invoke-trigger-on-behalf-of-player + */ + static void InvokeTriggerOnBehalfOfPlayer(const FInvokeTriggerResponse& OnCompletedRequest, FString Name, int PlayerId); +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h new file mode 100644 index 0000000..ea81c2d --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/LootLockerSrvPersitentDataHolder.h @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerSrvPersitentDataHolder.generated.h" + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerSrvPersitentDataHolder : public UObject +{ + GENERATED_BODY() + public: + static FString Token; + static FString ServerToken; + static FString CachedLastEndpointUsed; + static FString CachedLastRequestTypeUsed; + static FString CachedLastDataSentToServer; + static FString CachedSteamToken; + static FString CachedPlayerIdentifier; + static FString AdminToken; +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h new file mode 100644 index 0000000..117cd8f --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAssetRequest.h @@ -0,0 +1,260 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "LootLockerServerHttpClient.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerAssetRequest.generated.h" + +UENUM(BlueprintType) +enum class ELootLockerServerAssetFilter : uint8 +{ + None = 0, + Purchasable = 1, + NonPurchasable = 2, + Rentable = 3, + NonRentable = 4, + Popular = 5, + UnPopular = 6 +}; + +//TODO: implement default loadouts +// USTRUCT(BlueprintType) +// struct FLootLockerServerDefaultLoadouts { +// GENERATED_BODY() +// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") +// bool Light_Vehicle; +// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") +// bool Heavy_Vehicle; +// }; + +USTRUCT(BlueprintType) +struct FLootLockerServerLinks { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString thumbnail; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPersistentStorageItem { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString key; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerRarity { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString short_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString color; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPsn { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString entitlement_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 service_label; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAppStore +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString product_id; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGooglePay +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString product_id; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerExternalIdentifiers { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerPsn psn; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAppStore apple_app_store; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAppStore google_play; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerRentalOption { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 duration; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString sales_price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerLinks links; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerFile { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString url; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray tags; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerFilter { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAssetCandidate +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int created_by_player_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString created_by_player_uid; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerVariation +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString primary_color; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString secondary_color; + //TODO: implement properties + // TArray properties; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerLinks links; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAsset +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool active; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool purchasable; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString sales_price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString display_price; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString context; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString unlocks_context; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool detachable; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString updated; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString marked_new; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 default_variation_id; + //TODO: implement default loadouts + // UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + // FLootLockerServerDefaultLoadouts default_loadouts; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString description; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerLinks links; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray storage; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRarity rarity; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool popular; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 popularity_score; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool unique_instance; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerExternalIdentifiers external_identifiers; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray rental_options; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray filters; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray files; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray data_entities; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAssetCandidate asset_candidate; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray variations; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool featured; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool context_locked; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool initially_purchasable; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetAssetsToGameResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FServerAssetsResponseDelegateBP, FLootLockerServerGetAssetsToGameResponse, AssetsResponse); + +DECLARE_DELEGATE_OneParam(FServerAssetsResponseDelegate, FLootLockerServerGetAssetsToGameResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerAssetRequest : public UObject +{ + GENERATED_BODY() + + public: + + ULootLockerServerAssetRequest(); + + static void GetAssetsToGame(int StartFromIndex, int ItemsCount, ELootLockerServerAssetFilter AssetFilter, bool IncludeUGC, const FServerAssetsResponseDelegateBP& OnCompletedRequestBP = FServerAssetsResponseDelegateBP(), const FServerAssetsResponseDelegate& OnCompletedRequest = FServerAssetsResponseDelegate()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h new file mode 100644 index 0000000..03ff6be --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerAuthRequest.h @@ -0,0 +1,51 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.h" +#include "JsonObjectConverter.h" +#include "LootLockerServerAuthRequest.generated.h" +/** + * + */ + +USTRUCT(BlueprintType) +struct FLootLockerServerAuthenticationRequest +{ + GENERATED_BODY() + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Startup Item") + FString game_version; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Startup Item") + bool development_mode; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAuthenticationResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString token; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString error; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FServerAuthResponseBP, FLootLockerServerAuthenticationResponse, Var); +DECLARE_DELEGATE_OneParam(FServerAuthResponse, FLootLockerServerAuthenticationResponse); + +UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent)) +class LOOTLOCKERSERVERSDK_API ULootLockerServerAuthRequest : public UObject +{ +public: + GENERATED_BODY() +public: + ULootLockerServerAuthRequest(); + +public: + static void StartSession(const FServerAuthResponseBP& OnCompletedRequestBP = FServerAuthResponseBP(), const FServerAuthResponse& OnCompletedRequest = FServerAuthResponse()); + static void MaintainSession(const FServerAuthResponseBP& OnCompletedRequestBP = FServerAuthResponseBP(), const FServerAuthResponse& OnCompletedRequest = FServerAuthResponse()); + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h new file mode 100644 index 0000000..83b7b4b --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerCharacterRequest.h @@ -0,0 +1,125 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.h" +#include "LootLockerServerPlayerRequest.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerCharacterRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerCharacter +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool default; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString name; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerCharacterInventory { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 variation_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString rental_option_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString mounted_at; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString acquisition_source; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRental rental; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerCharacterInventoryResponse : public FLootLockerServerResponse { + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPlayerCharactersResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetCharacterLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT() +struct FLootLockerServerEquipCharacterResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +USTRUCT() +struct FLootLockerServerUnequipCharacterResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FCharactersResponseBP, FLootLockerServerGetPlayerCharactersResponse, CharactersResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FCharacterInventoryResponseBP, FLootLockerServerCharacterInventoryResponse, InventoryResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FCharacterLoadoutResponseBP, FLootLockerServerGetCharacterLoadoutResponse, LoadoutResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FEquipResponseBP, FLootLockerServerEquipCharacterResponse, EquipResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUnequipResponseBP, FLootLockerServerUnequipCharacterResponse, UnequipResponse); +DECLARE_DELEGATE_OneParam(FCharactersResponse, FLootLockerServerGetPlayerCharactersResponse); +DECLARE_DELEGATE_OneParam(FCharacterInventoryResponse, FLootLockerServerCharacterInventoryResponse); +DECLARE_DELEGATE_OneParam(FCharacterLoadoutResponse, FLootLockerServerGetCharacterLoadoutResponse); +DECLARE_DELEGATE_OneParam(FEquipResponse, FLootLockerServerEquipCharacterResponse); +DECLARE_DELEGATE_OneParam(FUnequipResponse, FLootLockerServerUnequipCharacterResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerCharacterRequest : public UObject +{ + GENERATED_BODY() + public: + ULootLockerServerCharacterRequest(); + + static void GetPlayerCharacters(int PlayerId, const FCharactersResponseBP& OnCompletedRequestBP = FCharactersResponseBP(), const FCharactersResponse& OnCompletedRequest = FCharactersResponse()); + + static void GetInventoryToCharacter(int PlayerId, int CharacterId, const FCharacterInventoryResponseBP& OnCompletedRequestBP = FCharacterInventoryResponseBP(), const FCharacterInventoryResponse& OnCompletedRequest = FCharacterInventoryResponse()); + + static void GetCharacterLoadout(int PlayerId, int CharacterId, const FCharacterLoadoutResponseBP& OnCompletedRequestBP = FCharacterLoadoutResponseBP(), const FCharacterLoadoutResponse& OnCompletedRequest = FCharacterLoadoutResponse()); + + static void EquipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, const FEquipResponseBP& OnCompletedRequestBP = FEquipResponseBP(), const FEquipResponse& OnCompletedRequest = FEquipResponse()); + + static void UnequipAssetForCharacterLoadout(int PlayerId, int CharacterId, int InstanceId, const FUnequipResponseBP& OnCompletedRequestBP = FUnequipResponseBP(), const FUnequipResponse& OnCompletedRequest = FUnequipResponse()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h new file mode 100644 index 0000000..0c1b438 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerHeroesRequest.h @@ -0,0 +1,137 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + + +#include "LootLockerServerAssetRequest.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerPlayerRequest.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerHeroesRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerHero +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int hero_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_default; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString hero_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString character_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString class_name; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerHeroInventory { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 variation_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString rental_option_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString mounted_at; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString acquisition_source; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRental rental; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerHeroInventoryResponse : public FLootLockerServerResponse { + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPlayerHeroesResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetHeroLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT() +struct FLootLockerServerEquipHeroResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +USTRUCT() +struct FLootLockerServerUnequipHeroResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; + +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FHeroesResponseBP, FLootLockerServerGetPlayerHeroesResponse, HeroesResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FHeroInventoryResponseBP, FLootLockerServerHeroInventoryResponse, InventoryResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FHeroLoadoutResponseBP, FLootLockerServerGetHeroLoadoutResponse, LoadoutResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FEquipHeroResponseBP, FLootLockerServerEquipHeroResponse, EquipResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUnequipHeroResponseBP, FLootLockerServerUnequipHeroResponse, UnequipResponse); +DECLARE_DELEGATE_OneParam(FHeroesResponse, FLootLockerServerGetPlayerHeroesResponse); +DECLARE_DELEGATE_OneParam(FHeroInventoryResponse, FLootLockerServerHeroInventoryResponse); +DECLARE_DELEGATE_OneParam(FHeroLoadoutResponse, FLootLockerServerGetHeroLoadoutResponse); +DECLARE_DELEGATE_OneParam(FEquipHeroResponse, FLootLockerServerEquipHeroResponse); +DECLARE_DELEGATE_OneParam(FUnequipHeroResponse, FLootLockerServerUnequipHeroResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerHeroesRequest : public UObject +{ + GENERATED_BODY() + public: + ULootLockerServerHeroesRequest(); + + static void GetPlayerHeroes(int PlayerId, const FHeroesResponseBP& OnCompletedRequestBP = FHeroesResponseBP(), const FHeroesResponse& OnCompletedRequest = FHeroesResponse()); + + static void GetInventoryToHero(int PlayerId, int HeroId, const FHeroInventoryResponseBP& OnCompletedRequestBP = FHeroInventoryResponseBP(), const FHeroInventoryResponse& OnCompletedRequest = FHeroInventoryResponse()); + + static void GetHeroLoadout(int PlayerId, int HeroId, const FHeroLoadoutResponseBP& OnCompletedRequestBP = FHeroLoadoutResponseBP(), const FHeroLoadoutResponse& OnCompletedRequest = FHeroLoadoutResponse()); + + static void EquipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, const FEquipHeroResponseBP& OnCompletedRequestBP = FEquipHeroResponseBP(), const FEquipHeroResponse& OnCompletedRequest = FEquipHeroResponse()); + + static void UnequipAssetForHeroLoadout(int PlayerId, int HeroId, int InstanceId, const FUnequipHeroResponseBP& OnCompletedRequestBP = FUnequipHeroResponseBP(), const FUnequipHeroResponse& OnCompletedRequest = FUnequipHeroResponse()); + + static ULootLockerServerHttpClient* HttpClient; + +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h new file mode 100644 index 0000000..03edbcb --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerPlayerRequest.h @@ -0,0 +1,146 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + + +#include "LootLockerServerAssetRequest.h" +#include "LootLockerServerConfig.h" +#include "LootLockerServerHttpClient.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerPlayerRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerRental { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_rental; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString time_left; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString duration; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString is_active; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerInventory { + GENERATED_BODY() + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 instance_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 variation_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString rental_option_id; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString acquisition_source; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerAsset asset; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerRental rental; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerInventoryResponse : public FLootLockerServerResponse { + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAddAssetData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 asset_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 asset_variation_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 asset_rental_option_id; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerAddAssetResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPlayerLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerEquipAssetForPlayerLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerUnequipAssetForPlayerLoadoutResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int32 total; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FInventoryResponseBP, FLootLockerServerInventoryResponse, InventoryResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FAddAssetResponseBP, FLootLockerServerAddAssetResponse, AddAssetResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FGetPlayerLoadoutResponseBP, FLootLockerServerGetPlayerLoadoutResponse, GetPlayerLoadoutResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FEquipAssetResponseBP, FLootLockerServerEquipAssetForPlayerLoadoutResponse, EquipAssetResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUnequipAssetResponseBP, FLootLockerServerUnequipAssetForPlayerLoadoutResponse, UnequipAssetResponse); +DECLARE_DELEGATE_OneParam(FInventoryResponse, FLootLockerServerInventoryResponse); +DECLARE_DELEGATE_OneParam(FAddAssetResponse, FLootLockerServerAddAssetResponse); +DECLARE_DELEGATE_OneParam(FGetPlayerLoadoutResponse, FLootLockerServerGetPlayerLoadoutResponse); +DECLARE_DELEGATE_OneParam(FEquipAssetResponse, FLootLockerServerEquipAssetForPlayerLoadoutResponse); +DECLARE_DELEGATE_OneParam(FUnequipAssetResponse, FLootLockerServerUnequipAssetForPlayerLoadoutResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerPlayerRequest : public UObject +{ + GENERATED_BODY() + +public: + ULootLockerServerPlayerRequest(); + + static void GetInventory(int PlayerId, int StartFromIndex, int ItemsCount, const FInventoryResponseBP& OnCompletedRequestBP = FInventoryResponseBP(), const FInventoryResponse& OnCompletedRequest = FInventoryResponse()); + + static void AddAssetToPlayerInventory(int PlayerId, FLootLockerServerAddAssetData AddAssetData, const FAddAssetResponseBP& OnCompletedRequestBP = FAddAssetResponseBP(), const FAddAssetResponse& OnCompletedRequest = FAddAssetResponse()); + + static void GetPlayerLoadout(int PlayerId, const FGetPlayerLoadoutResponseBP& OnCompletedRequestBP = FGetPlayerLoadoutResponseBP(), const FGetPlayerLoadoutResponse& OnCompletedRequest = FGetPlayerLoadoutResponse()); + + static void EquipAssetForPlayerLoadout(int PlayerId, int InstanceId, const FEquipAssetResponseBP& OnCompletedRequestBP = FEquipAssetResponseBP(), const FEquipAssetResponse& OnCompletedRequest = FEquipAssetResponse()); + + static void UnequipAssetForPlayerLoadout(int PlayerId, int LoadoutId, const FUnequipAssetResponseBP& OnCompletedRequestBP = FUnequipAssetResponseBP(), const FUnequipAssetResponse& OnCompletedRequest = FUnequipAssetResponse()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h new file mode 100644 index 0000000..e03c833 --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerStorageRequest.h @@ -0,0 +1,115 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + +#include "LootLockerServerHttpClient.h" + +#include "LootLockerServerStorageRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerPlayerStorageItemData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString key; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_public; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPlayerStorageItem +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int player_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerGetPersistentStorageResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerKeyValueSet +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString key; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FString value; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_public; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int order; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPlayerStorageData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int player_id; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray sets; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerPersistentStorageRequestData +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray payload; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerUpdatePersistentStorageResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray items; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FGetPersistentStorageResponseBP, FLootLockerServerGetPersistentStorageResponse, GetPersistentStorageResponse); +DECLARE_DYNAMIC_DELEGATE_OneParam(FUpdatePersistentStorageResponseBP, FLootLockerServerUpdatePersistentStorageResponse, UpdatePersistentStorageResponse); +DECLARE_DELEGATE_OneParam(FGetPersistentStorageResponse, FLootLockerServerGetPersistentStorageResponse); +DECLARE_DELEGATE_OneParam(FUpdatePersistentStorageResponse, FLootLockerServerUpdatePersistentStorageResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerStorageRequest : public UObject +{ + GENERATED_BODY() + + public: + ULootLockerServerStorageRequest(); + + static void GetPersistentStorage(TArray PlayerIds, const FGetPersistentStorageResponseBP& OnCompletedRequestBP = FGetPersistentStorageResponseBP(), const FGetPersistentStorageResponse& OnCompletedRequest = FGetPersistentStorageResponse()); + static void UpdatePersistentStorage(FLootLockerServerPersistentStorageRequestData requestData, const FUpdatePersistentStorageResponseBP& OnCompletedRequestBP = FUpdatePersistentStorageResponseBP(), const FUpdatePersistentStorageResponse& OnCompletedRequest = FUpdatePersistentStorageResponse()); + + static ULootLockerServerHttpClient* HttpClient; +}; diff --git a/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h new file mode 100644 index 0000000..4fce6fe --- /dev/null +++ b/4.26/Plugins/LootLockerServerSDK/Source/LootLockerServerSDK/Public/ServerAPI/LootLockerServerTriggerRequest.h @@ -0,0 +1,67 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" + + +#include "LootLockerServerAssetRequest.h" +#include "LootLockerServerConfig.h" +#include "UObject/NoExportTypes.h" +#include "LootLockerServerTriggerRequest.generated.h" + +USTRUCT(BlueprintType) +struct FLootLockerServerLevel +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int level; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + bool is_prestige; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int xp_threshold; + +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerXP +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int previous; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + int current; +}; + +USTRUCT(BlueprintType) +struct FLootLockerServerInvokeTriggerResponse : public FLootLockerServerResponse +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + FLootLockerServerXP xp; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray levels; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "LootLocker") + TArray granted_assets; +}; + +DECLARE_DYNAMIC_DELEGATE_OneParam(FInvokeTriggerResponseBP, FLootLockerServerInvokeTriggerResponse, InvokeTriggerResponse); +DECLARE_DELEGATE_OneParam(FInvokeTriggerResponse, FLootLockerServerInvokeTriggerResponse); + +/** + * + */ +UCLASS() +class LOOTLOCKERSERVERSDK_API ULootLockerServerTriggerRequest : public UObject +{ + GENERATED_BODY() + public: + ULootLockerServerTriggerRequest(); + + static void InvokeTriggerOnBehalfOfPlayer(FString name, int PlayerId, const FInvokeTriggerResponseBP& OnCompletedRequestBP = FInvokeTriggerResponseBP(), const FInvokeTriggerResponse& OnCompletedRequest = FInvokeTriggerResponse()); + + static ULootLockerServerHttpClient* HttpClient; +};