question

Chad avatar image
Chad asked

How to setup Player Data and change player data in Azure Function

Hello!

Considering CloudScript is long gone now, I was curious on how to setup PlayerData and how to adjust changes to PlayerData (UE4).

A scenario:

Say a new user has logged into the game for the first time. I want to add their Steam Username to the PlayerData.

The data would be this really: Key: username Value: <your-username>

I know how to setup on the PlayFab side (Automation->Create a function->Logged in payload->assign AzureFunction URL).

I just don't know what to do on the Azure Function side in C#. Can anyone show me an example on reading and writing this data in Azure Functions?

Player DataTitle DatadataCharacter Data
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Made Wang avatar image
Made Wang answered

Refer to Player Data - PlayFab | Microsoft Docs, PlayerData can be updated directly on the client side, and ReadOnlyData and InternalData need to be updated on the AzureFunction or server side.

Please refer to PlayFab CloudScript using Azure Functions Quickstart Guide - PlayFab | Microsoft Docs to configure the environment, refer to the following code to implement the functions you want, and use ExecuteFunction to call them on the client.

[FunctionName("UpdateUserData")]
public static async Task<dynamic> UpdateUserData(
[HttpTrigger(AuthorizationLevel.Function,"post", Route = null)] HttpRequest req, ILogger log)
{
    FunctionExecutionContext<dynamic> context = JsonConvert.DeserializeObject<FunctionExecutionContext<dynamic>>(await req.ReadAsStringAsync());
    dynamic args = context.FunctionArgument;
    string key = args["key"];
    string value =args["value"];
    var updateUserDataRequest = new UpdateUserDataRequest
    {
        PlayFabId = context.CallerEntityProfile.Lineage.MasterPlayerAccountId,
        Data = new Dictionary<string, string>()
        {
             { key, value }
        }
    };
    var settings = new PlayFabApiSettings
    {
        TitleId = context.TitleAuthenticationContext.Id,
        DeveloperSecretKey = Environment.GetEnvironmentVariable("PLAYFAB_DEV_SECRET_KEY", EnvironmentVariableTarget.Process)
    };
    var authContext = new PlayFabAuthenticationContext
    {
        EntityToken = context.TitleAuthenticationContext.EntityToken
    };
    var serverApi = new PlayFabServerInstanceAPI(settings,authContext);
    return await serverApi.UpdateUserDataAsync(updateUserDataRequest);
}

[FunctionName("GetUserData")]
public static async Task<dynamic> GetUserData(
    [HttpTrigger(AuthorizationLevel.Function,"post", Route = null)] HttpRequest req, ILogger log)
{
    FunctionExecutionContext<dynamic> context = JsonConvert.DeserializeObject<FunctionExecutionContext<dynamic>>(await req.ReadAsStringAsync());
    var args = context.FunctionArgument;
    string key = args["key"];
    var getUserDataRequest = new GetUserDataRequest
    {
        PlayFabId = context.CallerEntityProfile.Lineage.MasterPlayerAccountId,
        Keys=new List<string>
        {
            key
        }
    };
    var settings = new PlayFabApiSettings
    {
        TitleId = context.TitleAuthenticationContext.Id,
        DeveloperSecretKey = Environment.GetEnvironmentVariable("PLAYFAB_DEV_SECRET_KEY", EnvironmentVariableTarget.Process)
    };
    var authContext = new PlayFabAuthenticationContext
    {
        EntityToken = context.TitleAuthenticationContext.EntityToken
    };
    var serverApi = new PlayFabServerInstanceAPI(settings,authContext);
    var getUserDataResult =await serverApi.GetUserDataAsync(getUserDataRequest);
    return new
    {
        getUserDataResult.Result.Data[key].Value
    };
}

6 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Chad avatar image Chad commented ·

Here I have the Azure function URL and the Function name to test the call. However, whenever it's called, I get an error that says,

"CloudScriptAzureFunctionsHTTPRequestError"

"Invocation of cloud script function TestFunction failed"

I believe that this is successfully calling it nonetheless, but I was curious on what I need to apply for the function parameters? I'm a little bit lost at this placement

0 Likes 0 ·
issue1.png (21.4 KiB)
issue2.png (192.5 KiB)
Made Wang avatar image Made Wang Chad commented ·

Please provide more error messages, you can refer to the figure below to output specific error message.

Has your AzureFunctionCloudScript been published to Azure? If so, you can refer to Debugging CloudScript using Azure Functions with the Azure portal - PlayFab | Microsoft Docs for log information.

If you can, I hope you can share your AzureFunctionCloudScript, which will help us troubleshoot this issue.

0 Likes 0 ·
sample.png (66.2 KiB)
Chad avatar image Chad commented ·

I've also tried to update the data like this as well with the given Azure function. Same error

0 Likes 0 ·
issue3.png (229.2 KiB)
issue4.png (46.2 KiB)
Chad avatar image Chad commented ·

Can confirm it's calling, but it's still not accepting the values as params when calling the function like I'm doing in the Blueprint call

Log From streaming the call:

2021-12-29T07:07:52  Welcome, you are now connected to log-streaming service. The default timeout is 2 hours. Change the timeout with the App Setting SCM_LOGSTREAM_TIMEOUT (in seconds). 

2021-12-29T07:08:21.347 [Information] Executing 'MessageTest' (Reason='This function was programmatically called via the host APIs.', Id=ffde1a2a-db12-43e0-8457-2d9577a07bbb)
2021-12-29T07:08:21.349 [Information] I said a thing
2021-12-29T07:08:21.402 [Error] Executed 'MessageTest' (Failed, Id=ffde1a2a-db12-43e0-8457-2d9577a07bbb, Duration=8ms)
Value cannot be null. (Parameter 'key')

0 Likes 0 ·
Made Wang avatar image Made Wang Chad commented ·

I don't know much about how to pass parameters in UE4.You can try to use Postman to call AzureFunction.If you still have problems in Postman, you need to provide the code of AzureFunction so that we can further troubleshoot.

0 Likes 0 ·
Chad avatar image Chad commented ·

One last thing:

I did fix the blueprint side. Made a silly mistake, but I'm still producing the error that is from the previous comments above

0 Likes 0 ·
issue5.png (138.2 KiB)
Chiranjeevi avatar image
Chiranjeevi answered

My generic version of using Get and update UserData, UserReadonlyData

using PlayFab.ServerModels;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Company.PlayfabCloudScript.Util
{
    public class PlayerUserData
    {
        public static async Task SetUserDataForKeys(string playFabId,  Dictionary<string, string> newKeys, string permission, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            var request = new UpdateUserDataRequest()
            {
                PlayFabId = playFabId,
                Data = newKeys,
                Permission = permission
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.UpdateUserDataAsync(request);

            if (result.Error != null)
            {
                throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");
            }
        }

        public static async Task<Dictionary<string, string>> GetUserDataForKeys(string playFabId, List<string> keys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            
            var request = new GetUserDataRequest()
            {
                PlayFabId = playFabId,
                Keys = keys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.UpdateUserDataAsync(request);

            if (result.Error != null)
            {
                //throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");

                return new Dictionary<string, string>(){};
            }

            return result.Result.Data;
        }
        
        public static async Task SetUserReadOnlyDataForKeys(string playFabId,  Dictionary<string, string> newKeys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            var request = new UpdateUserDataRequest()
            {
                PlayFabId = playFabId,
                Data = newKeys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.GetUserDataAsync(request);

            if (result.Error != null)
            {
                throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");
            }
        }

        public static async Task<Dictionary<string, string>> GetUserReadOnlyDataForKeys(string playFabId, List<string> keys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            
            var request = new GetUserDataRequest()
            {
                PlayFabId = playFabId,
                Keys = keys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.GetUserReadOnlyDataAsync(request);

            if (result.Error != null)
            {
                //throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");

                return new Dictionary<string, string>(){};
            }

            return result.Result.Data;
        }
    }

}

3 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Chad avatar image Chad commented ·

Hello!!

I would love to give this a go, but I have issues with the Permission variable


            var request = new UpdateUserDataRequest()
            {
                PlayFabId = playFabId,
                Data = newKeys,
                Permission = permission ///ERROR, Cannot accept string
            };


Does not accept Permission as a string. Is there something else I should apply instead?


Also,

PlayFabServerInstanceAPI
Couldn't be found. What using namespace to I need to include?
0 Likes 0 ·
Chiranjeevi avatar image Chiranjeevi Chad commented ·

Permission is just a string, either public or private.

"PlayFabServerInstanceAPI Couldn't be found" - Yes i do getting this error and the same thing was recommended by Playfab developers.

var serverApi =new PlayFabServerInstanceAPI(settings,authContext);

@Made Wang pls address this question.

0 Likes 0 ·
Chiranjeevi avatar image Chiranjeevi Chad commented ·

If string not accepting, then go with enum like UserDataPermission.Public

public enum UserDataPermission
{
	Private,		
	Public	
}
0 Likes 0 ·
Chiranjeevi avatar image
Chiranjeevi answered

@Chad, here the updated version of the PlayerUserData.cs.

PlayFabServerInstanceAPI comes under Playfab namespace.

using PlayFab;
using PlayFab.ServerModels;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Company.PlayfabCloudScript.Util
{
    public class PlayerUserData
    {
        public static async Task SetUserDataForKeys(string playFabId,  Dictionary<string, string> newKeys, UserDataPermission keyPermission, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            var request = new UpdateUserDataRequest()
            {
                PlayFabId = playFabId,
                Data = newKeys,
                Permission = keyPermission
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.UpdateUserDataAsync(request);

            if (result.Error != null)
            {
                throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");
            }
        }

        public static async Task<Dictionary<string, PlayFab.ServerModels.UserDataRecord>> GetUserDataForKeys(string playFabId, List<string> keys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            
            var request = new GetUserDataRequest()
            {
                PlayFabId = playFabId,
                Keys = keys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.GetUserDataAsync(request);

            if (result.Error != null)
            {
                //throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");

                return new Dictionary<string, PlayFab.ServerModels.UserDataRecord>(){};
            }

            return result.Result.Data;
        }
        
        public static async Task SetUserReadOnlyDataForKeys(string playFabId,  Dictionary<string, string> newKeys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            var request = new UpdateUserDataRequest()
            {
                PlayFabId = playFabId,
                Data = newKeys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.UpdateUserReadOnlyDataAsync(request);

            if (result.Error != null)
            {
                throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");
            }
        }

        public static async Task<Dictionary<string, PlayFab.ServerModels.UserDataRecord>> GetUserReadOnlyDataForKeys(string playFabId, List<string> keys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            
            var request = new GetUserDataRequest()
            {
                PlayFabId = playFabId,
                Keys = keys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.GetUserReadOnlyDataAsync(request);

            if (result.Error != null)
            {
                //throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");

                return new Dictionary<string, PlayFab.ServerModels.UserDataRecord>(){};
            }

            return result.Result.Data;
        }
    }

}

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Chiranjeevi avatar image
Chiranjeevi answered

@Chad, here the updated version of the PlayerUserData.cs.

PlayFabServerInstanceAPI comes under Playfab namespace.

using PlayFab;
using PlayFab.ServerModels;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Company.PlayfabCloudScript.Util
{
    public class PlayerUserData
    {
        public static async Task SetUserDataForKeys(string playFabId,  Dictionary<string, string> newKeys, UserDataPermission keyPermission, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            var request = new UpdateUserDataRequest()
            {
                PlayFabId = playFabId,
                Data = newKeys,
                Permission = keyPermission
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.UpdateUserDataAsync(request);

            if (result.Error != null)
            {
                throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");
            }
        }

        public static async Task<Dictionary<string, PlayFab.ServerModels.UserDataRecord>> GetUserDataForKeys(string playFabId, List<string> keys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            
            var request = new GetUserDataRequest()
            {
                PlayFabId = playFabId,
                Keys = keys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.GetUserDataAsync(request);

            if (result.Error != null)
            {
                //throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");

                return new Dictionary<string, PlayFab.ServerModels.UserDataRecord>(){};
            }

            return result.Result.Data;
        }
        
        public static async Task SetUserReadOnlyDataForKeys(string playFabId,  Dictionary<string, string> newKeys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            var request = new UpdateUserDataRequest()
            {
                PlayFabId = playFabId,
                Data = newKeys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.UpdateUserReadOnlyDataAsync(request);

            if (result.Error != null)
            {
                throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");
            }
        }

        public static async Task<Dictionary<string, PlayFab.ServerModels.UserDataRecord>> GetUserReadOnlyDataForKeys(string playFabId, List<string> keys, PlayFabApiSettings apiSettings, PlayFabAuthenticationContext authenticationContext)
        {            
            
            var request = new GetUserDataRequest()
            {
                PlayFabId = playFabId,
                Keys = keys
            };

            var serverApi = new PlayFabServerInstanceAPI(apiSettings, authenticationContext);

            var result = await serverApi.GetUserReadOnlyDataAsync(request);

            if (result.Error != null)
            {
                //throw new Exception($"An error occured while add new user data: {result.Error.GenerateErrorReport()}");

                return new Dictionary<string, PlayFab.ServerModels.UserDataRecord>(){};
            }

            return result.Result.Data;
        }
    }

}

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.