question

Flarvain avatar image
Flarvain asked

How to convert my json database

Hi there,

New to Playfab but have found it a nice and intuitive system to use so thanks!

I built my game originally to be offline but as i wanted to bring it online i needed a backend service to start running some server authoritative code which bought me to playfab.

My game:

It's quite similar in layout to a mobile game like clash of clans, you can have buildings, they have their own stats as well as an X and Y coordinate to say where they are in your base.

My question is three parts so apologies for the length.

Question 1 - where / how do i save and also load my json data.

At the moment i write this all to json and save it locally then load it in each playthrough. the data looks similar to this:

{"sceneData":{"items":[{"instanceId":1,"itemId":3635,"posX":28,"posZ":20,"currentLv":1},{"instanceId":2,"itemId":2496,"posX":22,"posZ":17,"currentLv":1},{"instanceId":59892,"itemId":4764,"posX":21,"posZ":11,"currentLv":1},{"instanceId":63502,"itemId":7666,"posX":26,"posZ":19,"currentLv":1},{"instanceId":26143,"itemId":1502,"posX":23,"posZ":25,"currentLv":1}]}}

I then have a supporting scriptable object that has all the configuration/item data about my itemId, so that i can keep a reduced DB size and not have to save alot per itemID:

I don't think i can keep this system when the game goes online, while i don't understand client hacking too well, i believe they could just hack a new value into the production rate as an example, and they would then be making way more. So i think this data also needs to be going up into the cloud.

I was reading a different thread about someone with a similar json structure and them trying to save it to playerdata as a read only string. They were encouraged by staff to rather try using the catalog / inventory system instead. I tend to agree with this because of the frequency i'll be making updates to things like position, i wouldn't want to be pulling back the data for my entire town every time i dragged a building. (i could mitigate this with only saving on exit etc but i digress).

So i started with a building like this:

I wrote some server code:

handlers.AddBuilding = function (args, context) 

{server.GrantItemsToUser({PlayFabId: currentPlayerId, ItemIds: "3265", CatalogVersion: "Buildings"})
  var results = result.ItemGrantResults; //get the results   

var instId = results[0].ItemInstanceId; //get the instance ID to be adding custom data to.
  

server.UpdateUserInventoryItemCustomData ({ PlayFabId: currentPlayerId, ItemInstanceId: instId, Data: {"Name":"GoldMine","Max Building Lv":"10","Grid Size":"3","Build Time":"30","Production Rate":"200","Product Max":"500","Power":"100","Product":"gold"} }); 
}; //add my custom data.

Client code:

    public void AddBuildingStats() // will need to take a parameter of ID.
    {
        PlayFabClientAPI.ExecuteCloudScript(new ExecuteCloudScriptRequest()
        {
            FunctionName = "AddBuilding", //arbitrary function name (must exist in your uploaded cloud.js file --- the automation area)
            //FunctionParameter = i don't have the parameters yet, still working on making it work.
            GeneratePlayStreamEvent = true, //shows it in the playstream console.
        }, OnAddBuildingSuccess, //successful HTTP response.
        OnErrorShared); //failed HTTP response.
    }


    public void OnAddBuildingSuccess(ExecuteCloudScriptResult result)
    {
        Debug.Log("Hey you added a building!");
        //actually build the building from your other code.
    }

My challenge is, i'm not sure how i can take my inventory items and convert it back into the json structure the game currently runs on.

Question 2 - The Configuration data

I'm not sure where i store the configuration data. it is all static values, but i need to have it in the cloud so that i can reference in my server code. Aka, a user wants go from a level 1 - 2 building, The client would submit 'level up' request along with the building ID. i would then need to check virtual currency, and some of the configuration data to determine the buildings new values as well as some other sense checks.

Question 3 - Virtual currency on new inventory item

I want to be reducing virtual currency based on the inventory item's cost. I can see there's a setup for this in the catalog item, just not sure how to reference it. once i have that i suppose it would be storing it as a var and subtracting it with SubtractUserVirtualCurrency

Apologies for all the text - i've changed the method of storing and saving data many times so i figured if i give you as much information you can guide me in the right direction.

Thanks,

Player DatadataPlayer Inventory
6gzqq.png (82.9 KiB)
5s8no.png (34.6 KiB)
10 |1200

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

Citrus Yan avatar image
Citrus Yan answered

For question 1, may I know the specific problem you’re facing when converting inventory items to the json structure in the game? Like, what’s the exact json structure of the inventory items and the one runs on the game, the code you’re using to convert it, the error you’re experiencing when converting them?

For question 2, Title Data is designed to store the configuration data where all clients have access to, please navigate to the link to learn more.

For question 3, you can use GetCatalogItems to retrieve all the items’ info in a specific catalog, all the data along with each item will be returned, including their VC prices you set previously, which you can reference to. And, in normal cases, there is no need to manually subtract VC when purchasing an item, you can use the PurchaseItem API to perform the purchasing process, the item will be added to the player’s inventory and its associated VC will be subtracted automatically once succeed.

1 comment
10 |1200

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

Flarvain avatar image Flarvain commented ·

not used to this posting method. @Citrus Yan - I've posted a response below.

0 Likes 0 ·
Flarvain avatar image
Flarvain answered

Hi Citrus,

Thank you for the reply - I've taken a while to think over it while i further develop this and i have some follow up questions:

Firstly, the things i have resolved:

In question 3 with the VC - The PurchaseItem API wasn't a fit for what i was doing because it only allows you to charge a single currency, in my game i need to charge multiple currencies for an item. e.g. 300 wood and 500 gold = a building.

I solved this with the following...

Client side:

    public void RequestBuildingPurchasePlayFab(int _itemID)
    {
        string ItemIDs = _itemID.ToString();
        PlayFabClientAPI.ExecuteCloudScript(new ExecuteCloudScriptRequest()
        {
            FunctionName = "PurchaseBuilding", //arbitrary function name (must exist in your uploaded cloud.js file --- the automation area)
            FunctionParameter = new { ItemID = ItemIDs },
            GeneratePlayStreamEvent = true, //shows it in the playstream console.
        }, OnAddBuildingSuccess, //successful HTTP response.
        OnErrorShared); //failed HTTP response.
    }


    public void OnAddBuildingSuccess(ExecuteCloudScriptResult result)
    {
        string itemInstanceID = result.FunctionResult.ToString();


        BaseItemScript item = SceneManager.instance.AddItem(3265, itemInstanceID, false, true); // will need to find a way to return 
        if (item != null)
        {
            DataBaseManager.instance.UpdateItemData(item);
        }
        CheckCurrencyPlayFab(); // update and refresh your currencies.
    }

Server Side:

handlers.PurchaseBuilding = function(args,context) {
    
//get your currenies...
   var inventory = server.GetUserInventory({ PlayFabId: currentPlayerId });
   var currentGold = inventory.VirtualCurrency["GD"]; // this will change to be dynamic in future.
   
//Return catalog of Buildings and search for your item ID, once you find it.
    var catalog = server.GetCatalogItems({ CatalogVersion: "Buildings" });
    var catalogItem = null;
    for (var c = 0; c < catalog.Catalog.length; c++) {
        if (args.ItemID === catalog.Catalog[c].ItemId)
            catalogItem = catalog.Catalog[c];
    }
//throw an error if you don't find the catalog or catalogitem for your ItemID.
    if (!catalogItem)
        throw "Catalog Item not found";
   
//get the currency price of my catalog item.
    if (catalogItem.VirtualCurrencyPrices.hasOwnProperty("GD")) //dynamic in future
        buyPrice = catalogItem.VirtualCurrencyPrices["GD"]; //dynamic in future
   
//Step 1. Check you have the currency to make the purchase.
//Step 1.2 [FOR LATER] Check you haven't already got the maximum number of these buildings. Looping through Inventory and checking number of itemID that you have and returning an int. then compare that int to the catalogItem's custom data of maximum number of buildings.
   
   if (currentGold >= buyPrice)
   {
        var addVirtualItem = server.GrantItemsToUser({PlayFabId: currentPlayerId, ItemIds: args.ItemID, CatalogVersion: "Buildings"})
        server.SubtractUserVirtualCurrency({PlayFabId: currentPlayerId, Amount: buyPrice, VirtualCurrency: "GD"})
        
        return addVirtualItem.ItemGrantResults[0].ItemInstanceId;
   }


}

For question 2 - i'll look into that after i fix question 1.

Question 1 - The database reads the following data:

InstanceID, ItemID, PosX, PosY, CurrentLv

Currently this is a local json file in a streaming assets folder. However, i would like to rather have this returned from cloud code when the user loads the game.

Each building is an inventory item and each Inventory item holds all the data i need to be returning.

For example InstanceID is my inventory item's instanceID, ItemID is my inventory item's ID, PosX, PosY and currentLv are all custom data stored against the inventory item.

What i'm looking to do when a user connects to the game is create all this information through some Automation code, store it in a var and then return it to the client in this format.

{"sceneData":{"items":[{"instanceId":2FF4E9B6A0DEBC50,"itemId":3635,"posX":28,"posZ":20,"currentLv":1},{"instanceId":99CE43253ADF39FD,"itemId":2496,"posX":22,"posZ":17,"currentLv":1},{"instanceId":89A3277E3B284236,"itemId":4764,"posX":21,"posZ":11,"currentLv":1},{"instanceId":2364E04B37239020,"itemId":7666,"posX":26,"posZ":19,"currentLv":1},{"instanceId":24KL43123BGD56QS,"itemId":1502,"posX":23,"posZ":25,"currentLv":1}]}}

That is the part i don't know how to do, would you be able to assist me in writing that function?

Thanks,

Flarvain

1 comment
10 |1200

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

Citrus Yan avatar image Citrus Yan commented ·

I think it's quite straightforward: on the client side use the client GetUserInventory API to retrieve all items (along with their custom data) from the current player's inventory, and use a for loop to store them in a var.

0 Likes 0 ·

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.

Error rendering WebPanel (widgets/consolidation-widget.ftl): org.hibernate.hql.internal.ast.QuerySyntaxException: AvailableConsolidation is not mapped [from AvailableConsolidation up where up.node = :node]