// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #include "UserGameplayData/AwsGameKitUserGameplayData.h" // GameKit #include "AwsGameKitCore.h" #include "AwsGameKitRuntime.h" #include "AwsGameKitRuntimeInternalHelpers.h" #include "AwsGameKitRuntimePublicHelpers.h" #include "Core/AwsGameKitErrors.h" // Unreal #include "Async/Async.h" #include "Templates/Function.h" UserGameplayDataLibrary AwsGameKitUserGameplayData::GetUserGameplayDataLibraryFromModule() { UE_LOG(LogAwsGameKit, Display, TEXT("AwsGameKitUserGameplayData::GetUserGameplayDataLibraryFromModule()")); FAwsGameKitRuntimeModule* runtimeModule = FModuleManager::GetModulePtr("AwsGameKitRuntime"); return runtimeModule->GetUserGameplayDataLibrary(); } void AwsGameKitUserGameplayData::AddBundle(const FUserGameplayDataBundle& userGameplayDataBundle, TAwsGameKitDelegateParam ResultDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; int32 pairCount = userGameplayDataBundle.BundleMap.Num(); IntResult result; // Instantiate struct to contain any unprocessed items that may be passed back FUserGameplayDataBundle unprocessedBundleItems; if (pairCount == 0) { UE_LOG(LogAwsGameKit, Error, TEXT("UAwsGameKitUserGameplayDataCallableWrapper::AddUserGameplayData - The bundle is empty.")); result.Result = GameKit::GAMEKIT_ERROR_USER_GAMEPLAY_DATA_PAYLOAD_INVALID; result.ErrorMessage = "The bundle is empty"; } else { // Preallocate with expected size to prevent pointers becoming invalid // when array expands and data is copied. TArray bundleItemKeys; bundleItemKeys.SetNum(pairCount); TArray bundleItemKeysChrArray; bundleItemKeysChrArray.SetNum(pairCount); TArray bundleItemValues; bundleItemValues.SetNum(pairCount); TArray bundleItemValuesChrArray; bundleItemValuesChrArray.SetNum(pairCount); int i = 0; for (auto it = userGameplayDataBundle.BundleMap.begin(); it != userGameplayDataBundle.BundleMap.end(); ++it) { const FString& key = it.Key(); const FString& value = it.Value(); bundleItemKeys[i] = TCHAR_TO_UTF8(*key); bundleItemValues[i] = TCHAR_TO_UTF8(*value); bundleItemKeysChrArray[i] = bundleItemKeys[i].c_str(); bundleItemValuesChrArray[i] = bundleItemValues[i].c_str(); i++; } std::string bundleName = TCHAR_TO_UTF8(*userGameplayDataBundle.BundleName); // In the case there is an unprocessed item, assign the bundle that it is a part of unprocessedBundleItems.BundleName = bundleName.c_str(); UserGameplayDataBundle wrapperArgs { bundleName.c_str(), (bundleItemKeysChrArray.GetData()), (bundleItemValuesChrArray.GetData()), size_t(pairCount) }; result = IntResult(library.UserGameplayDataWrapper->GameKitAddUserGameplayData(library.UserGameplayDataInstanceHandle, unprocessedBundleItems.BundleMap, wrapperArgs)); } InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, ResultDelegate, result, unprocessedBundleItems); }); } void AwsGameKitUserGameplayData::SetClientSettings(const FUserGameplayDataClientSettings& clientSettings) { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); UserGameplayDataClientSettings settings; settings.ClientTimeoutSeconds = clientSettings.ClientTimeoutSeconds; settings.RetryIntervalSeconds = clientSettings.RetryIntervalSeconds; settings.MaxRetryQueueSize = clientSettings.MaxRetryQueueSize; settings.MaxRetries = clientSettings.MaxRetries; settings.RetryStrategy = clientSettings.RetryStrategy; settings.MaxExponentialRetryThreshold = clientSettings.MaxExponentialRetryThreshold; settings.PaginationSize = clientSettings.PaginationSize; library.UserGameplayDataWrapper->GameKitSetUserGameplayDataClientSettings(library.UserGameplayDataInstanceHandle, settings); } void AwsGameKitUserGameplayData::ListBundles(TAwsGameKitDelegateParam&> ResultDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; TArray bundles; IntResult result(library.UserGameplayDataWrapper->GameKitListUserGameplayDataBundles(library.UserGameplayDataInstanceHandle, bundles)); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, ResultDelegate, result, bundles); }); } void AwsGameKitUserGameplayData::GetBundle(const FString& UserGameplayDataBundleName, TAwsGameKitDelegateParam ResultDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; FUserGameplayDataBundle bundle; bundle.BundleName = UserGameplayDataBundleName; IntResult result(library.UserGameplayDataWrapper->GameKitGetUserGameplayDataBundle(library.UserGameplayDataInstanceHandle, bundle.BundleMap, TCHAR_TO_UTF8(*UserGameplayDataBundleName))); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, ResultDelegate, result, bundle); }); } void AwsGameKitUserGameplayData::GetBundleItem(const FUserGameplayDataBundleItem& userGameplayDataBundleItem, TAwsGameKitDelegateParam ResultDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; FUserGameplayDataBundleItemValue bundleItem; bundleItem.BundleName = userGameplayDataBundleItem.BundleName; bundleItem.BundleItemKey = userGameplayDataBundleItem.BundleItemKey; FAwsGameKitInternalTempStrings ConvertString; UserGameplayDataBundleItem wrapperArgs { ConvertString(userGameplayDataBundleItem.BundleName), ConvertString(userGameplayDataBundleItem.BundleItemKey) }; IntResult result(library.UserGameplayDataWrapper->GameKitGetUserGameplayDataBundleItem(library.UserGameplayDataInstanceHandle, bundleItem.BundleItemValue, wrapperArgs)); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, ResultDelegate, result, bundleItem); }); } void AwsGameKitUserGameplayData::UpdateItem(const FUserGameplayDataBundleItemValue& userGameplayDataBundleItemValue, FAwsGameKitStatusDelegateParam OnCompleteDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; FAwsGameKitInternalTempStrings ConvertString; UserGameplayDataBundleItemValue wrapperArgs { ConvertString(*userGameplayDataBundleItemValue.BundleName), ConvertString(*userGameplayDataBundleItemValue.BundleItemKey), ConvertString(*userGameplayDataBundleItemValue.BundleItemValue) }; IntResult result(library.UserGameplayDataWrapper->GameKitUpdateUserGameplayDataBundleItem(library.UserGameplayDataInstanceHandle, wrapperArgs)); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, OnCompleteDelegate, result); }); } void AwsGameKitUserGameplayData::DeleteAllData(FAwsGameKitStatusDelegateParam OnCompleteDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; IntResult result(library.UserGameplayDataWrapper->GameKitDeleteAllUserGameplayData(library.UserGameplayDataInstanceHandle)); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, OnCompleteDelegate, result); }); } void AwsGameKitUserGameplayData::DeleteBundle(const FString& UserGameplayDataBundleName, FAwsGameKitStatusDelegateParam OnCompleteDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; IntResult result(library.UserGameplayDataWrapper->GameKitDeleteUserGameplayDataBundle(library.UserGameplayDataInstanceHandle, TCHAR_TO_UTF8(*UserGameplayDataBundleName))); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, OnCompleteDelegate, result); }); } void AwsGameKitUserGameplayData::DeleteBundleItems(const FUserGameplayDataDeleteItemsRequest& userGameplayDataBundleItemsDeleteRequest, FAwsGameKitStatusDelegateParam OnCompleteDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; IntResult result; if (userGameplayDataBundleItemsDeleteRequest.BundleItemKeys.Num() == 0 || userGameplayDataBundleItemsDeleteRequest.BundleName.IsEmpty()) { UE_LOG(LogAwsGameKit, Error, TEXT("UAwsGameKitUserGameplayDataCallableWrapper::DeleteUserGameplayDataBundleItems - The bundle is invalid.")); result.Result = GameKit::GAMEKIT_ERROR_USER_GAMEPLAY_DATA_PAYLOAD_INVALID; result.ErrorMessage = "The bundle is invalid"; } else { // Preallocate with expected size to prevent pointers becoming invalid // when array expands and data is copied. int32 numKeys = userGameplayDataBundleItemsDeleteRequest.BundleItemKeys.Num(); TArray bundleItemKeys; bundleItemKeys.SetNum(numKeys); TArray bundleItemKeysChrArray; bundleItemKeysChrArray.SetNum(numKeys); for (int i = 0; i < numKeys; i++) { auto& itemKey = userGameplayDataBundleItemsDeleteRequest.BundleItemKeys[i]; bundleItemKeys[i] = TCHAR_TO_UTF8(*itemKey); bundleItemKeysChrArray[i] = bundleItemKeys[i].c_str(); } std::string bundleName = TCHAR_TO_UTF8(*userGameplayDataBundleItemsDeleteRequest.BundleName); UserGameplayDataDeleteItemsRequest wrapperArgs { bundleName.c_str(), (bundleItemKeysChrArray.GetData()), size_t(numKeys) }; result = library.UserGameplayDataWrapper->GameKitDeleteUserGameplayDataBundleItems(library.UserGameplayDataInstanceHandle, wrapperArgs); } InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, OnCompleteDelegate, result); }); } void AwsGameKitUserGameplayData::StartRetryBackgroundThread() { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); library.UserGameplayDataWrapper->GameKitUserGameplayDataStartRetryBackgroundThread(library.UserGameplayDataInstanceHandle); } void AwsGameKitUserGameplayData::StopRetryBackgroundThread() { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); library.UserGameplayDataWrapper->GameKitUserGameplayDataStopRetryBackgroundThread(library.UserGameplayDataInstanceHandle); } void AwsGameKitUserGameplayData::DropAllCachedEvents() { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); library.UserGameplayDataWrapper->GameKitUserGameplayDataDropAllCachedEvents(library.UserGameplayDataInstanceHandle); } void AwsGameKitUserGameplayData::SetNetworkChangeDelegate(const FNetworkStatusChangeDelegate& networkStatusChangeDelegate) { if (networkStatusChangeDelegate.IsBound()) { FAwsGameKitRuntimeModule* runtimeModule = FModuleManager::GetModulePtr("AwsGameKitRuntime"); runtimeModule->SetNetworkChangeDelegate(networkStatusChangeDelegate); UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); library.UserGameplayDataWrapper->GameKitUserGameplayDataSetNetworkChangeCallback(library.UserGameplayDataInstanceHandle, runtimeModule, &FAwsGameKitRuntimeModule::OnNetworkStatusChangeDispatcher::Dispatch); } } void AwsGameKitUserGameplayData::SetCacheProcessedDelegate(const FCacheProcessedDelegate& cacheProcessedDelegate) { if (cacheProcessedDelegate.IsBound()) { FAwsGameKitRuntimeModule* runtimeModule = FModuleManager::GetModulePtr("AwsGameKitRuntime"); UserGameplayDataLibrary library = runtimeModule->GetUserGameplayDataLibrary(); library.UserGameplayDataStateHandler->SetCacheProcessedDelegate(cacheProcessedDelegate); auto cacheProcessedDelegateExecutor = [](const bool isCacheProcessed) { FAwsGameKitRuntimeModule* runtimeModule = FModuleManager::GetModulePtr("AwsGameKitRuntime"); UserGameplayDataLibrary library = runtimeModule->GetUserGameplayDataLibrary(); library.UserGameplayDataStateHandler->onCacheProcessedDelegate.ExecuteIfBound(isCacheProcessed); }; typedef LambdaDispatcher CacheProcessedDelegateExecuter; library.UserGameplayDataWrapper->GameKitUserGameplayDataSetCacheProcessedCallback(library.UserGameplayDataInstanceHandle, (void*)&cacheProcessedDelegateExecutor, CacheProcessedDelegateExecuter::Dispatch); } } void AwsGameKitUserGameplayData::PersistToCache(const FString& cacheFile, FAwsGameKitStatusDelegateParam OnCompleteDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; IntResult result(library.UserGameplayDataWrapper->GameKitUserGameplayDataPersistApiCallsToCache(library.UserGameplayDataInstanceHandle, TCHAR_TO_UTF8(*cacheFile))); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, OnCompleteDelegate, result); }); } void AwsGameKitUserGameplayData::LoadFromCache(const FString& cacheFile, FAwsGameKitStatusDelegateParam OnCompleteDelegate) { InternalAwsGameKitRunLambdaOnWorkThread([=] { UserGameplayDataLibrary library = GetUserGameplayDataLibraryFromModule(); FGraphEventRef OrderedWorkChain; IntResult result(library.UserGameplayDataWrapper->GameKitUserGameplayDataLoadApiCallsFromCache(library.UserGameplayDataInstanceHandle, TCHAR_TO_UTF8(*cacheFile))); InternalAwsGameKitRunDelegateOnGameThread(OrderedWorkChain, OnCompleteDelegate, result); }); }