// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
// Standard Library
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
// GameKit
using AWS.GameKit.Runtime.Core;
using AWS.GameKit.Runtime.FeatureUtils;
using AWS.GameKit.Runtime.Models;
using AWS.GameKit.Runtime.Utils;
namespace AWS.GameKit.Runtime.Features.GameKitUserGameplayData
{
///
/// User Gameplay Data wrapper for GameKit C++ SDK calls
///
public class UserGameplayDataWrapper : GameKitFeatureWrapperBase
{
// Select the correct source path based on the platform
#if UNITY_IPHONE && !UNITY_EDITOR
private const string IMPORT = "__Internal";
#else
private const string IMPORT = "aws-gamekit-user-gameplay-data";
#endif
// Delegates
protected static UserGameplayData.NetworkChangedDelegate _networkCallbackDelegate;
protected static UserGameplayData.CacheProcessedDelegate _cacheProcessedDelegate;
// Network state
protected static bool _isNetworkHealthy = true;
// DLL loading
[DllImport(IMPORT)] private static extern IntPtr GameKitUserGameplayDataInstanceCreateWithSessionManager(IntPtr sessionManager, FuncLoggingCallback logCb);
[DllImport(IMPORT)] private static extern void GameKitUserGameplayDataInstanceRelease(IntPtr userGameplayDataInstance);
[DllImport(IMPORT)] private static extern void GameKitSetUserGameplayDataClientSettings(IntPtr userGameplayDataInstance, UserGameplayDataClientSettings settings);
[DllImport(IMPORT)] private static extern uint GameKitAddUserGameplayData(IntPtr userGameplayDataInstance, UserGameplayDataBundle userGameplayDataBundle, IntPtr unprocessedItemsReceiver, FuncKeyValueStringCallback unprocessedItemsCallback);
[DllImport(IMPORT)] private static extern uint GameKitListUserGameplayDataBundles(IntPtr userGameplayDataInstance, IntPtr dispatchReceiver, FuncStringCallback responseCb);
[DllImport(IMPORT)] private static extern uint GameKitGetUserGameplayDataBundle(IntPtr userGameplayDataInstance, string bundleName, IntPtr dispatchReceiver, FuncKeyValueStringCallback responseCb);
[DllImport(IMPORT)] private static extern uint GameKitGetUserGameplayDataBundleItem(IntPtr userGameplayDataInstance, UserGameplayDataBundleItem userGameplayDataBundleItem, IntPtr dispatchReceiver, FuncStringCallback responseCb);
[DllImport(IMPORT)] private static extern uint GameKitUpdateUserGameplayDataBundleItem(IntPtr userGameplayDataInstance, UserGameplayDataBundleItemValue userGameplayDataBundleItemValue);
[DllImport(IMPORT)] private static extern uint GameKitDeleteAllUserGameplayData(IntPtr userGameplayDataInstance);
[DllImport(IMPORT)] private static extern uint GameKitDeleteUserGameplayDataBundle(IntPtr userGameplayDataInstance, string bundleName);
[DllImport(IMPORT)] private static extern uint GameKitDeleteUserGameplayDataBundleItems(IntPtr userGameplayDataInstance, UserGameplayDataDeleteItemsRequest deleteItemsRequest);
[DllImport(IMPORT)] private static extern void GameKitUserGameplayDataStartRetryBackgroundThread(IntPtr userGameplayDataInstance);
[DllImport(IMPORT)] private static extern void GameKitUserGameplayDataStopRetryBackgroundThread(IntPtr userGameplayDataInstance);
[DllImport(IMPORT)] private static extern void GameKitUserGameplayDataSetNetworkChangeCallback(IntPtr userGameplayDataInstance, IntPtr dispatchReceiver, FuncNetworkStatusChangeCallback statusChangeCallback);
[DllImport(IMPORT)] private static extern void GameKitUserGameplayDataSetCacheProcessedCallback(IntPtr userGameplayDataInstance, IntPtr dispatchReceiver, FuncCacheProcessedCallback cacheProcessedCallback);
[DllImport(IMPORT)] private static extern void GameKitUserGameplayDataDropAllCachedEvents(IntPtr userGameplayDataInstance);
[DllImport(IMPORT)] private static extern uint GameKitUserGameplayDataPersistApiCallsToCache(IntPtr userGameplayDataInstance, string offlineCacheFile);
[DllImport(IMPORT)] private static extern uint GameKitUserGameplayDataLoadApiCallsFromCache(IntPtr userGameplayDataInstance, string offlineCacheFile);
[AOT.MonoPInvokeCallback(typeof(FuncNetworkStatusChangeCallback))]
protected static void NetworkStatusChangeCallback(IntPtr dispatchReceiver, bool isConnectionOk, string connectionClient)
{
NetworkStatusChangeResults results = new NetworkStatusChangeResults();
results.IsConnectionOk = isConnectionOk;
results.ConnectionClient = connectionClient;
results.ResultCode = GameKitErrors.GAMEKIT_SUCCESS;
_isNetworkHealthy = isConnectionOk;
_networkCallbackDelegate(results);
}
[AOT.MonoPInvokeCallback(typeof(FuncCacheProcessedCallback))]
protected static void CacheProcessedCallback(IntPtr dispatchReceiver, bool isCacheProcessed)
{
CacheProcessedResults results = new CacheProcessedResults();
results.IsCacheProcessed = isCacheProcessed;
results.ResultCode = GameKitErrors.GAMEKIT_SUCCESS;
_cacheProcessedDelegate(results);
}
public void SetUserGameplayDataClientSettings(UserGameplayDataClientSettings settings)
{
DllLoader.TryDll(() => GameKitSetUserGameplayDataClientSettings(GetInstance(), settings), nameof(GameKitSetUserGameplayDataClientSettings));
}
public AddUserGameplayDataResults AddUserGameplayData(AddUserGameplayDataDesc addUserGameplayDataDesc)
{
IntPtr[] bundleItemKeys = Marshaller.ArrayOfStringsToArrayOfIntPtr(addUserGameplayDataDesc.BundleItems.Keys.ToArray());
IntPtr[] bundleItemValues = Marshaller.ArrayOfStringsToArrayOfIntPtr(addUserGameplayDataDesc.BundleItems.Values.ToArray());
GCHandle bundleItemKeysPin = GCHandle.Alloc(bundleItemKeys, GCHandleType.Pinned);
GCHandle bundleItemValuesPin = GCHandle.Alloc(bundleItemValues, GCHandleType.Pinned);
UserGameplayDataBundle userGameplayDataBundle;
userGameplayDataBundle.BundleName = addUserGameplayDataDesc.BundleName;
userGameplayDataBundle.NumKeys = (IntPtr)addUserGameplayDataDesc.BundleItems.Count;
userGameplayDataBundle.BundleItemKeys = bundleItemKeysPin.AddrOfPinnedObject();
userGameplayDataBundle.BundleItemValues = bundleItemValuesPin.AddrOfPinnedObject();
MultiKeyValueStringCallbackResult callbackResults = new MultiKeyValueStringCallbackResult();
uint status = GameKitErrors.GAMEKIT_ERROR_GENERAL;
try
{
status = DllLoader.TryDll(callbackResults, (IntPtr dispatchReceiver) => GameKitAddUserGameplayData(GetInstance(), userGameplayDataBundle, dispatchReceiver, GameKitCallbacks.MultiKeyValueStringCallback), nameof(GameKitAddUserGameplayData), GameKitErrors.GAMEKIT_ERROR_GENERAL);
}
finally
{
bundleItemKeysPin.Free();
bundleItemValuesPin.Free();
Marshaller.FreeArrayOfIntPtr(bundleItemKeys);
Marshaller.FreeArrayOfIntPtr(bundleItemValues);
}
AddUserGameplayDataResults results = new AddUserGameplayDataResults();
for (uint i = 0; i < callbackResults.ResponseKeys.Count(); ++i)
{
results.BundleItems.Add(callbackResults.ResponseKeys[i], callbackResults.ResponseValues[i]);
}
results.ResultCode = status;
return results;
}
public MultiStringCallbackResult ListUserGameplayDataBundles()
{
MultiStringCallbackResult results = new MultiStringCallbackResult();
uint status = DllLoader.TryDll(results, (IntPtr dispatchReceiver) => GameKitListUserGameplayDataBundles(GetInstance(), dispatchReceiver, GameKitCallbacks.MultiStringCallback), nameof(GameKitListUserGameplayDataBundles), GameKitErrors.GAMEKIT_ERROR_GENERAL);
results.ResultCode = status;
return results;
}
public GetUserGameplayDataBundleResults GetUserGameplayDataBundle(string bundleName)
{
MultiKeyValueStringCallbackResult callbackResults = new MultiKeyValueStringCallbackResult();
uint status = DllLoader.TryDll(callbackResults, (IntPtr dispatchReceiver) => GameKitGetUserGameplayDataBundle(GetInstance(), bundleName, dispatchReceiver, GameKitCallbacks.MultiKeyValueStringCallback), nameof(GameKitGetUserGameplayDataBundle), GameKitErrors.GAMEKIT_ERROR_GENERAL);
GetUserGameplayDataBundleResults results = new GetUserGameplayDataBundleResults();
for (uint i = 0; i < callbackResults.ResponseKeys.Count(); ++i)
{
results.Bundles.Add(callbackResults.ResponseKeys[i], callbackResults.ResponseValues[i]);
}
results.ResultCode = status;
return results;
}
public StringCallbackResult GetUserGameplayDataBundleItem(UserGameplayDataBundleItem userGameplayDataBundleItem)
{
StringCallbackResult results = new StringCallbackResult();
uint status = DllLoader.TryDll(results, (IntPtr dispatchReceiver) => GameKitGetUserGameplayDataBundleItem(GetInstance(), userGameplayDataBundleItem, dispatchReceiver, GameKitCallbacks.StringCallback), nameof(GameKitGetUserGameplayDataBundleItem), GameKitErrors.GAMEKIT_ERROR_GENERAL);
results.ResultCode = status;
return results;
}
public uint UpdateUserGameplayDataBundleItem(UserGameplayDataBundleItemValue userGameplayDataBundleItemValue)
{
return DllLoader.TryDll(() => GameKitUpdateUserGameplayDataBundleItem(GetInstance(), userGameplayDataBundleItemValue), nameof(GameKitUpdateUserGameplayDataBundleItem), GameKitErrors.GAMEKIT_ERROR_GENERAL);
}
public uint DeleteAllUserGameplayData()
{
return DllLoader.TryDll(() => GameKitDeleteAllUserGameplayData(GetInstance()), nameof(GameKitDeleteAllUserGameplayData), GameKitErrors.GAMEKIT_ERROR_GENERAL);
}
public uint DeleteUserGameplayDataBundle(string bundleName)
{
return DllLoader.TryDll(() => GameKitDeleteUserGameplayDataBundle(GetInstance(), bundleName), nameof(GameKitDeleteUserGameplayDataBundle), GameKitErrors.GAMEKIT_ERROR_GENERAL);
}
public uint DeleteUserGameplayDataBundleItems(DeleteUserGameplayDataBundleItemsDesc deleteUserGameplayDataBundleItemsDesc)
{
IntPtr[] bundleItemKeys = Marshaller.ArrayOfStringsToArrayOfIntPtr(deleteUserGameplayDataBundleItemsDesc.BundleItemKeys);
GCHandle bundleKeysPin = GCHandle.Alloc(bundleItemKeys, GCHandleType.Pinned);
UserGameplayDataDeleteItemsRequest userGameplayDataDeleteItemsRequest = new UserGameplayDataDeleteItemsRequest();
userGameplayDataDeleteItemsRequest.BundleName = deleteUserGameplayDataBundleItemsDesc.BundleName;
userGameplayDataDeleteItemsRequest.NumKeys = (IntPtr)deleteUserGameplayDataBundleItemsDesc.NumKeys;
userGameplayDataDeleteItemsRequest.BundleItemKeys = bundleKeysPin.AddrOfPinnedObject();
try
{
return DllLoader.TryDll(() => GameKitDeleteUserGameplayDataBundleItems(GetInstance(), userGameplayDataDeleteItemsRequest), nameof(GameKitDeleteUserGameplayDataBundleItems), GameKitErrors.GAMEKIT_ERROR_GENERAL);
}
finally
{
bundleKeysPin.Free();
Marshaller.FreeArrayOfIntPtr(bundleItemKeys);
}
}
public void UserGameplayDataStartRetryBackgroundThread()
{
DllLoader.TryDll(() => GameKitUserGameplayDataStartRetryBackgroundThread(GetInstance()), nameof(GameKitUserGameplayDataStartRetryBackgroundThread));
}
public void UserGameplayDataStopRetryBackgroundThread()
{
DllLoader.TryDll(() => GameKitUserGameplayDataStopRetryBackgroundThread(GetInstance()), nameof(GameKitUserGameplayDataStopRetryBackgroundThread));
}
public void ForceRetry(Action callback)
{
UserGameplayDataStopRetryBackgroundThread();
UserGameplayData.NetworkChangedDelegate tempNetworkChangeDelegate = null;
tempNetworkChangeDelegate = (NetworkStatusChangeResults results) =>
{
// This temporary delegate must unsubscribe itself otherwise the captured
// objects could remain in memory longer than needed.
_networkCallbackDelegate -= tempNetworkChangeDelegate;
callback(results.IsConnectionOk);
};
_networkCallbackDelegate += tempNetworkChangeDelegate;
UserGameplayDataStartRetryBackgroundThread();
}
public void UserGameplayDataSetNetworkChangeCallback(UserGameplayData.NetworkChangedDelegate networkChangeDelegate)
{
_networkCallbackDelegate += networkChangeDelegate;
DllLoader.TryDll(() => GameKitUserGameplayDataSetNetworkChangeCallback(GetInstance(), IntPtr.Zero, NetworkStatusChangeCallback), nameof(GameKitUserGameplayDataSetNetworkChangeCallback));
}
public bool GetLastNetworkHealthState()
{
return _isNetworkHealthy;
}
public void UserGameplayDataSetCacheProcessedCallback(UserGameplayData.CacheProcessedDelegate cacheProcessedDelegate)
{
_cacheProcessedDelegate = cacheProcessedDelegate;
DllLoader.TryDll(() => GameKitUserGameplayDataSetCacheProcessedCallback(GetInstance(), IntPtr.Zero, CacheProcessedCallback), nameof(GameKitUserGameplayDataSetCacheProcessedCallback));
}
public void UserGameplayDataDropAllCachedEvents()
{
DllLoader.TryDll(() => GameKitUserGameplayDataDropAllCachedEvents(GetInstance()), nameof(GameKitUserGameplayDataDropAllCachedEvents));
}
public uint UserGameplayDataPersistApiCallsToCache(string offlineCacheFile)
{
return DllLoader.TryDll(() => GameKitUserGameplayDataPersistApiCallsToCache(GetInstance(), offlineCacheFile), nameof(GameKitUserGameplayDataPersistApiCallsToCache), GameKitErrors.GAMEKIT_ERROR_GENERAL);
}
public uint UserGameplayDataLoadApiCallsFromCache(string offlineCacheFile)
{
return DllLoader.TryDll(() => GameKitUserGameplayDataLoadApiCallsFromCache(GetInstance(), offlineCacheFile), nameof(GameKitUserGameplayDataLoadApiCallsFromCache), GameKitErrors.GAMEKIT_ERROR_GENERAL);
}
protected override IntPtr Create(IntPtr sessionManager, FuncLoggingCallback logCb)
{
return DllLoader.TryDll(() => GameKitUserGameplayDataInstanceCreateWithSessionManager(sessionManager, logCb), nameof(GameKitUserGameplayDataInstanceCreateWithSessionManager), IntPtr.Zero);
}
protected override void Release(IntPtr instance)
{
DllLoader.TryDll(() => GameKitUserGameplayDataInstanceRelease(instance), nameof(GameKitUserGameplayDataInstanceRelease));
}
}
}