Sign In Register

How can we help you today?

Start a new topic
Answered

BEGINNER 1: Data persistence and cloud code. Saving, loading and simple fight calculations.

Hello! First of all, my apologies for sending a ticket regarding development help, rather than asking on the forum. 

I noticed Rules regarding tickets and forum posts after I had already posted the ticket. My bad.


Anyway...I've been diving into Gamesparks for the past few days. Originally, I started a year ago with another game, but - life happened :)

A lot has changed since, so I'm pretty much starting over.


I will start posting these in the form of forum posts, each separate with it's own title - for easier commenting later.

In this post, I'd like to discuss patterns of saving data, accessing it and doing some simple cloud code calculations, completely avoiding the client.


DATA PERSISTENCE

Gamesparks has en excellent tutorial on how to do partial updates on complex JSON documents. This allows for easy structured updates using LogEventRequest(); 

This code takes a string path and a JSON value as input and updates a collection under player id with the defined object.

var progressCollection = Spark.runtimeCollection("player_progress");
var inputPath = Spark.data.PATH;
var inputValue = Spark.data.VAL;
var updateObject = {};
updateObject[inputPath] = inputValue;
progressCollection.update( {"_id": Spark.getPlayer().getPlayerId()}, {"$set" : updateObject}, true, false);

Using something like this on the test harness...

{
 "@class": ".LogEventRequest",
 "eventKey": "UPDATE",
 "PATH": "Building3.Health",
 "VAL": 15
}

results in 

{
 "@class": ".LogEventResponse",
 "scriptData": {
  "data": [
   {
    "_id": "568eea25e4b0444487f23e2e",
    "Soldier": {
     "Damage": 1
    },
    "Building3": {
     "Health": 15
    }
   }
  ]
 }
}

which is pretty neat. Note: the soldier was created earlier. Now that we have our data, we can send the attack event.   

// FUNCTIONS
function GetHealth(inputPath)
{
    var matchObject = {"$match" : {"_id": Spark.getPlayer().getPlayerId()}};
    var pathArray = inputPath.split(".");
    var lastPathString = pathArray[pathArray.length - 1];
    var projectData = {"_id":0 };
    projectData[lastPathString] = "$"+inputPath;
    var projectObject = {"$project" : projectData};
    var partialResult = progressCollection.aggregate(matchObject, projectObject);

    return partialResult[0][inputPath]["Health"];
}
function GetDamage(inputPath)
{
    var matchObject = {"$match" : {"_id": Spark.getPlayer().getPlayerId()}};
    var pathArray = inputPath.split(".");
    var lastPathString = pathArray[pathArray.length - 1];
    var projectData = {"_id":0 };
    projectData[lastPathString] = "$"+inputPath;
    var projectObject = {"$project" : projectData};
    var partialResult = progressCollection.aggregate(matchObject, projectObject);

    return partialResult[0][inputPath]["Damage"];
}

// LOGIC

var progressCollection = Spark.runtimeCollection("player_progress");
var myPath = Spark.data.PATH;
var targetPath = Spark.data.TPATH;

var health = GetHealth(targetPath)
var damage = GetDamage(myPath);
var hitResult = health - damage;

// UPDATE
var req = new SparkRequests.LogEventRequest();
req.eventKey = "UPDATE";
req.PATH = targetPath+".Health";
req.VAL = hitResult;
req.Execute();
Spark.setScriptData("hitResult", hitResult);

This event takes 2 arguments, targetPath and myPath (should be re-factored to attackerPath), as input. GetHealth(string) and GetDamage(string) return values of corresponding records in the document using the path string. 

{
 "@class": ".LogEventRequest",
 "eventKey": "ATTACK",
 "PATH": "Soldier",
 "TPATH": "Building3"
}

It returns the values, calculates the damage done (health going below 0 is not handled yet) and calls the UPDATE event (described at the beginning) and finally return the result to the client. 

{
 "@class": ".LogEventResponse",
 "scriptData": {
  "hitResult": 14
 }
}

Good job Gamesparks!


QUESTIONS:

1. Could you provide an example using FindAndModify instead of update using the same functionality, if possible. Here I primarily mean the neat way of adding stuff with dot notation. Also, I would appreciate if you'd give an example of FindOne() instead of aggregate?

2. How frequent can I send attack event using this code?

3. Is it possible to add unique ids to these elements using the platform or should the collection be organized differently. My first idea was to use insert, which will give a unique id to an element added (soldier, house, etc...) and add a "owner" : "playerId" field to each object inserted. Would that be the correct approach? Or maybe, keep info about soldiers, houses etc in a runtime collection and as players play, add them to player.scriptData(). Do objects in player scriptData have unique ID?



The reason I would like unique IDs on these elements is because I'd like the client to be aware of random letters only, instead of meaningful data. This will be my next topic.


Cheers,

Pavle


Best Answer

I have recreated your behavior in the test harness. 


To answer question 1:  

Spark.runtimeCollection("player_progress").findAndModify({"_id" : Spark.getPlayer().getPlayerId()}, {"$set" : {
    "doors" : 15,
    "roof"  : 20
    
}});

 

In regard to this cloud code: buildings is present in the document.  I used your update progress event / cloud code to add buildings. Using the above, we amend  the document and add doors /roofs. These fields are arbitrary and can be variables through which you can pass anything you want. 


Question 2: The way I handle the sending of frequent events is the following: 

                 Go to Configurator-> Cloud Code-> System -> Every Minute. Any code in here will run every minute. If you desire you can reference modules (See documentation) or just hard code the behavior you want. If this is not quick enough there is another option but it does have redundancies (a scheduler can only spawn so many other schedulers ) to protect us server side. Here is a link to the documentation :  https://docs.gamesparks.net/howtos/cloud-code-scripting/how-to-schedule-cloud-code-scripts


Question 3: I'm a bit confused about the question. The short answer is no , objects in player scriptdata do not have unique ID's. However, you can work around by using  new Date().getTime; 

 This returns the epoch / unix time stamp down to the millisecond. I imagine that could be considered unique and maybe you could reference that? 

Regards, Patrick. 


No problem at all. 


Glad to be of help, Patrick. 


Awesome reply.

The work-around with the date should do the job.


Thanks, Patrick! 

Answer

I have recreated your behavior in the test harness. 


To answer question 1:  

Spark.runtimeCollection("player_progress").findAndModify({"_id" : Spark.getPlayer().getPlayerId()}, {"$set" : {
    "doors" : 15,
    "roof"  : 20
    
}});

 

In regard to this cloud code: buildings is present in the document.  I used your update progress event / cloud code to add buildings. Using the above, we amend  the document and add doors /roofs. These fields are arbitrary and can be variables through which you can pass anything you want. 


Question 2: The way I handle the sending of frequent events is the following: 

                 Go to Configurator-> Cloud Code-> System -> Every Minute. Any code in here will run every minute. If you desire you can reference modules (See documentation) or just hard code the behavior you want. If this is not quick enough there is another option but it does have redundancies (a scheduler can only spawn so many other schedulers ) to protect us server side. Here is a link to the documentation :  https://docs.gamesparks.net/howtos/cloud-code-scripting/how-to-schedule-cloud-code-scripts


Question 3: I'm a bit confused about the question. The short answer is no , objects in player scriptdata do not have unique ID's. However, you can work around by using  new Date().getTime; 

 This returns the epoch / unix time stamp down to the millisecond. I imagine that could be considered unique and maybe you could reference that? 

Regards, Patrick. 

Hi Pavle, 


Thanks for your feedback on the platform, always good to hear.


 I'm currently recreating your example in the portal. Thanks to your comprehensive example I should be back to you soon with an answer.

Regards, Patrick.

Login to post a comment