question

kwinegardner avatar image
kwinegardner asked

Looking for a simple example of using GetTitleData within a Cloud Script

I want to be able to grant users some virtual currency within a cloud script and keep the configuration data of events associated with currency reward amounts (eventId:amount kvp) within the title's Content.

Let's assume I author my rewards schedule within my game's Content-TitleData as key=VCCodeEventId, value=AmountToGrant and I would have a Cloud Script execute upon the occurrence of an event to AddUserVirtualCurrency based on this. Given that the documentation appears to provide only C# examples, I'd very much appreciate a Cloud Script example of using GetTitleData.

Taking this the next step further, I would introduce cooldowns for having received the reward for a given EventId (UpdateUserInternalData seems like the perfect place to set consumption flags and timestamps).

Thanks.

CloudScriptTitle 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.

1 Answer

·
Seth Du avatar image
Seth Du answered

Here is a sample:

First of all, you will maintain an entry in Title Data that stores the information of VC granting event. Then you will need Cloud Script functions to validate the player (cooldowns and etc.), and write custom Event (using title data).

  • Title Data:

Key = “VC1_loginreward”

Value = 200

  • Player internal data (title):

Key = “LoginRewardTimestamp”

Value = 1555049051135 //you can get this via getTime() in JavaScript.

  • Cloud Script
handlers.loginReward = function (context) {
    var verificationFlag = false;
    var myTime = new Date();
    var Now = myTime.getTime();
    
//get title data
    var TitleDataRequest = {"Keys":["VC_loginreward"]};
    var TitleDataResponse = server.GetTitleData(TitleDataRequest);
    if(!TitleDataResponse.Data.hasOwnProperty("VC_loginreward"))
    {
        log.error("Event notfound. Exiting..."); 
        return JSON.stringify([]);
    }
    else
    {
	var VC_loginreward = TitleDataResponse.Data.VC_loginreward;
    }

//get player data
    var playerDataRequest = {"PlayFabId": currentPlayerId,"Keys":["LoginRewardTimestamp"]};
    var playerDataResponse = server.GetUserInternalData(playerDataRequest);
    if(!playerDataResponse.Data.hasOwnProperty("LoginRewardTimestamp"))
    {
	// Not found means this is the first time to grant reward, just record the time
	server.UpdateUserInternalData(
	{
	    "PlayFabId":currentPlayerId,
	    "Data":{"LoginRewardTimestamp":Now}
	}
	);
	verificationFlag = true;
    }
    else
    {
	var timestamp =  playerDataResponse.Data.LoginRewardTimestamp;
	//validate cooldown
	if (Now - timestamp > 300000000) // 3.4 days
        {
	    verificationFlag = true;
	}
    }

    if (verificationFlag == true)
    {
	server.AddUserVirtualCurrency(
        {
	    "PlayFabId":currentPlayerId,
	    "VirtualCurrency":"VC",
	    "Amount": VC_loginreward
    	});

	//generate custom playstream event

	server.WritePlayerEvent({"EventName":"VC_loginreward","PlayFabId":currentPlayerId});
    }

}

Still, It is just a very simple example and many places are hard coded. If you just want to know the API usage in Cloud Script, this works fine. But granting the VC according to custom events is not recommended and unsafe because the PlayStream events can be duplicated. In my sample, you only need to call WritePlayerEvent API, to grant the VC (amount is defined in the title data). Please feel free to reply this answer so that we can provide more help.

11 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.

kwinegardner avatar image kwinegardner commented ·

Thank you for the response. This was exactly what I was looking for. Regarding the safety aspect of this setup, what would be the safest way to trigger the grants? We have other places where we are triggering the increment of Player Statistics with the occurrence of custom PlayStream events... I suppose those too would be ruined by duped events.

0 Likes 0 ·
Seth Du avatar image Seth Du ♦ kwinegardner commented ·

You are right, it is not safe. Since the WritePlayerEvent is a client API, there is a possibility of abusive uses. Setting cooldown is helpful but if this statistic is quite important (like player's ranking score), this will affect players' game experience. however, it will be fine if it is for like progressive(daily) rewards. The recommendation is that for the important data, please verify it in the Cloud Script or in your external secure server.

there are very few build-in PlayStream event that will occur once, like player_added_title, which will only appear once when the account is created in the title. You may make use of these events.

0 Likes 0 ·
kwinegardner avatar image kwinegardner kwinegardner commented ·

@SethDu - I'd like to clarify what the risks we are talking about. Is there a risk of WritePlayerEvent firing multiple times for a single client event generation (ie my code says execute WritePlayerEvent 3 times a day for a given event, but this could result in more than 3 occurrences of that event in event history (assuming my code makes no other calls to that event)? I am fully aware of the risks of users sniffing the outgoing API web requests and writing their own routines to try to send those requests multiple times. Is this latter problem the only "not safe" concern you're warning about or are there other risks?

Thanks.

0 Likes 0 ·
Seth Du avatar image Seth Du ♦ kwinegardner commented ·

Yes, what I have mentioned is that users may sniff and craft the same WritePlayerEvent call. But I think it is no big deal if you only record those events. The thing is that it will affect players' gaming experiences if you implement some Rules that are triggered by those events.

For example, I have written a rule that triggered by custom event "Entered_New_Chapter", players will be rewarded with some items. If a player sniff and craft the WritePlayerEvent API call, and keep sending the same event, the players will keep receiving the same items.

0 Likes 0 ·
Zack Blase avatar image Zack Blase commented ·

I've tried implementing this but the timestamp just gets returned as [object Object]. I looked in the player data and it's a super long number which I assume is correct, but do you have any idea as to why it doesn't return as a number? When I try to do the "Now - timestamp" operation, I get "NaN" as a result. Thank you

0 Likes 0 ·
Seth Du avatar image Seth Du ♦ Zack Blase commented ·

Can you show the corresponding part of your code? Have you tried to serialize the result?

0 Likes 0 ·
Zack Blase avatar image Zack Blase Seth Du ♦ commented ·

So, I'm trying to do something similar. I'm implementing a currency collection system where I need to store and get the last time the user "collected" said currency so I can calculate the amount that is to be collected (like Clash of Clans, if you're familiar).

handlers.collectMoney = function (args, context) {
    
        
    var myTime = new Date();
    var Now = myTime.getTime();
    var seconds = 0;
    var playerTimeDataRequest = {"PlayFabId": currentPlayerId, "Keys":["MoneyTimeStamp"]};
    var playerTimeDataResponse = server.GetUserData(playerTimeDataRequest);
    if(!playerTimeDataResponse.Data.hasOwnProperty("MoneyTimeStamp")) {
        server.UpdateUserData({"PlayFabId":currentPlayerId, "Data":{"MoneyTimeStamp":Now}});
        seconds = 86400000;
    }
    else {
        var timeStamp = playerTimeDataResponse.Data.MoneyTimeStamp;
        
        if (Now - timeStamp > 86400000) {
            seconds = 86400000;
        }
        else {
            seconds = Now - timeStamp;
        } 
    }
    server.UpdateUserData({"PlayFabId":currentPlayerId, "Data":{"MoneyTimeStamp":Now}});
    seconds = seconds / 1000;
   
    
    return {moneyAmount: moneyAmount};
};

0 Likes 0 ·
Show more comments

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.