question

brendan avatar image
brendan asked

UpdateUserInventoryItemCustomData doesn't work on the server!

Galla.adam
started a topic on Mon, 01 June 2015 at 5:52 AM

I wanted to use UpdateUserInventoryItemCustomData in cloudscript. I wanted to set the item's Position attribute. Here is my code:

server.UpdateUserInventoryItemCustomData({
    PlayFabId: currentPlayerId,
    ItemInstanceId: inventory[i].ItemInstanceId,
    Data: { Position: position }
});

This is part of a for loop, and the inventory[i] represent an item in the players inventory. The position variable is a serialized unity vector3 so a simple string.

I got this error:

TypeError: undefined is not a function
    at handlers.startCraft (main.js:80:15) ->                           server.UpdateUserInventoryItemCustomData({
    at __playfab_internal.invokeHandler (Script Document:4:153)
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

·
brendan avatar image
brendan answered

13 Comments
Brendan Vanous said on Wed, 03 June 2015 at 1:25 AM

Hi Adam,

All server calls should (of course) be accessible via Cloud Script - we'll check into this, and update this thread as soon as we have more info.

Brendan


Galla.adam said on Wed, 03 June 2015 at 1:58 AM

Thank you!

I'm waiting for your update!


Galla.adam said on Wed, 03 June 2015 at 6:01 AM

I also found another bug(?):

the documentation of GrantItemsToUser says the result contains the granted item instanceid. But it's always undefined.

I don't really understand how this GrantItemsToUser works. If I grant an item to the user, and then I iterate through his inventory the new item randomly appears in the list, or not.

var granResult = server.GrantItemsToUser({
    CatalogVersion: catalogVersion,
    PlayFabId: currentPlayerId,
    ItemIds: [itemID],
});

for(j = 0; j < inventory.length; j++)
{
    if(inventory[i].ItemId == args.ItemID)
    {
                // Sometimes find it sometimes not... but why?
         }
}

Why happens this?

Sorry for putting three different things into one thread.


Brendan Vanous said on Wed, 03 June 2015 at 7:07 PM

Hi again,

The JavaScript server has been updated, so the newest API calls (including UpdateUserInventoryItemCustomData) are all available. However, reviewing your original question, I do need to ask - why is it necessary to update all the item positions? If the position is based on the character's location in the game, why not just store this at the character level? Bear in mind that we do need to ensure titles aren't hitting the service with excessive calls (see our ToS for more info on this), as that wouldn't be sustainable.

For the GrantItemsToUser call, I do see that the InstanceId isn't being returned - we'll get that fixed and post here as soon as we've updated for that.

And as for the order items appear in the user's inventory, it isn't guaranteed to be in a particular order - you should assume that items can appear anywhere in the list.

Brendan


Galla.adam said on Thu, 04 June 2015 at 1:23 AM

Thank you!

I'm not updating the position of all the item. I only want to update the position of the granted item, but since this method doesnt return the instance id, I have to find it with a for loop.

I think I was tired yesterday, since just figured out how false was my logic, the inventory can contain one item id more than once, so this is not the right way to get it... well this happens sometime :D

I'll wait for your update then.

Ps: Maybe it's just me, but sometimes I would find useful if I could query one item even from the catalogs by itemID or from the inventory by ItemInstanceID. Mostly I would like to do this on the server, to validate an action


Brendan Vanous said on Thu, 04 June 2015 at 3:40 PM

We'll update here when we've got that fixed. For querying an individual item, this sounds like a good candidate for our Feature Requests forum. I will say this though - when working with a backend service, it is important to make sure you're not making a lot of little calls, since the overhead of the call itself can become the majority of your bandwidth. I won't go into a lengthy discussion on the specifics of packet overhead, but if you think of a packet as having an average of 40 bytes, that means that for a 40 byte response, 50% of your traffic is overhead - not very efficient, obviously. This gets especially critical in games like first-person shooters (for communications between players and between players and the game server), since bandwidth is at a premium in those cases. But even without a realtime session, network optimization is still something to consider in your game design.

Brendan


Galla.adam said on Fri, 05 June 2015 at 12:19 AM

Yes, that's right. I was thinking about something like this:
Imagine a standard strategy game, or bulding game. There are buildings that can create units for example.

Let say, we have only one building in the game :)
In this case at the start of the game I would download a list of the units that can create this building. When the player want to create onw of the unit, I'll send both the building and the unit ItemID to the server. There I want to check if the building is able to create this unit (this information stored in the building instance custom data) and I also want to know how much resources will need to create the unit (this is stored in the unit item in the catalog).

Right now I have to query all the buildings and make a loop to select the building with the sent ItemID, and I have to do this again with the Unit.

In this example the individual item query could be handy. Ok a for loop is not too resource-itense, but the code without of lots of loops looks nicer, and it's easier to read.


Brendan Vanous said on Sat, 06 June 2015 at 12:56 PM

So, to make sure I understand:

You have a game in which some entities can build other entities. The client is submitting "build unit B with unit A", and you need to check to see if this is a legitimate build request. My first question then is, why is the information on what the entities can build which other not also in the Catalog? This way, when you query for the game catalog, you would get all that info at once.


Galla.adam said on Mon, 08 June 2015 at 1:34 AM

Yes, that's right, but also there is a crafting or building time, so I have to store the craft start time, or the craft finish time.

I only make a request two cases:

  1. When the player log in, I made a request to query all the buildings, and check if there is a finished craft progress. (also with a queue handling).

  2. When the client notices one of the craft progress is about to finish (2 sec, since the communication time with the server is around 1-2 seconds), I make a request to check if this is a valid thing, and if the craft is finished on the server, it grants the item to the player.

Becouse I have to track the craft time, that is different from user to user, I can't store it in the catalog.


Brendan Vanous said on Tue, 09 June 2015 at 12:42 AM

I see - so you're storing the completion time in the Custom Data for the inventory item instance, correct? If a player could have many items all building other items simultaneously, this would require a lot of calls to get a relatively small amount of data. Instead, I would recommend storing this in User Read-Only data, and then read all the Keys using GetUserReadOnlyData with no Key set so that you get all of them in one call.

Back on GrantItemsToUser, I had some time for additional testing, and I see what's happening - this actually isn't a bug at all. It's our system informing you of an issue in the command. Since you can grant many items in one call, the way the response works is that for any item which was granted to the user, the ItemGrantResults parameter Results will be set to true. If Results is false, that means that no such ItemId exists in the specified game Catalog, and so it could not be granted. I'll get the docs updated to make this clearer.

Brendan


Galla.adam said on Tue, 09 June 2015 at 1:39 AM

Correct. But let me explain a little further:

I have buildings and items. Both of them has completion time.

The player can construct the buildings. when the player select and places the building in a certain spot, the game sends a request to the cloudscript, that does the following:

  1. Check if the player has the resources (currency), if he has the script subtracts the resources (it is possible that the building needs wood and stone (2 currency) both to buid, so the client side PurchaseItem is not right for me.

  2. If the currencies subtracted successfully, the script will grant the item to the user.

  3. I would like to set the position of the building at this point (position is where the player placed it), but for this I need the iteminstanceid of the granted item (i think?).

  4. At the end the cloudscript sends back to client the granted ItemInstanceId (the client set it to the created buildin), and the currencies balance

The completion time of this constructing is stored in the player custom data. The client know this time already, since this is stored in the catalog. When the client detects that the construction time is about to finish, it ask the cloudscript to do the completion function, that basicly removes the building completion times from the player data.

The Items can be crafted in the buildings. In this case this is the workflow:

  1. The player selects a building, and clicks on the item he want to craft.

  2. The client sends a request to the cloudscript, to check if the player has the currencies, and if he has it subtracts them.

  3. At this point the item is not yet granted to the user, only saved the itemid and the completion time to the building "currentCraftingItem" custom data or to the building "queue" custom data.

  4. The script only returns the new currency balance

Client also knows the completion times, and when it is about to finish it sends a new request and call the craftfinish function of the cloudscript. This will validate the time, then if it is finished it will grant the item to the user with the itemID stored in the building.

Do you think, I should use the user data for the item craft too?
How could I get the iteminstanceid for the building to store the position?


Brendan Vanous said on Wed, 10 June 2015 at 1:09 AM

Correct - I wasn't suggesting using PurchaseItem. You would, as you suggest, use GrantItemsToUser and SubtractUserVirtualCurrency. Then, store the item instance ID from the grant, the completion time (based on server time, so that the client can't cheat that), the position, and any other data needed in User Read-Only or User Internal Data, so that the client can't modify it. You would then check that data when the player makes the completion call.

Again, the ItemInstanceId is always returned on a successful item grant. Please re-check the "Results" parameter for the item. The only time you won't get an ItemInstanceId in the data returned for an item is if Results is false - which indicates that the item you attempted to grant does not exist in the given Catalog.

Brendan


Galla.adam said on Wed, 10 June 2015 at 1:21 AM

Oh, I see, thanks, I'll check the GrantItemsToUser again.

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.