Sign In Register

How can we help you today?

Start a new topic
Answered

Update partial of playerData

Hi All,


I have some playerData which stores some stats for different classes. They're called back and saved back pretty regularly but but done so in different pieces of cloudcode.


The thing im struggling with at the moment is when i setPrivateData(blah, "Warrior") i end up completely re-creating the warrior datafield. All i'd like to do is 'update' a few fields without deleting all the others.


I think i found a support ticket a while ago that used $set to achieve this but i can't find it anymore.


current example:


Simplified version, lets say i had a warrior and all he had was strength and a weapon. if i run cloudcode to update my weapon, it will delete out my strength. I just want to run an update on my weapon and keep strength the same.


Could someone please provide some small cloud code for how i would do this?


Thanks,

Ryan


Best Answer

Hey Ryan,


The basics of it are quite easy once you get used to it, here are some examples. 


Creating new GameData for 'Player'

var playerId = Spark.getPlayer().getPlayerId();
//Retrieve gaemspark data service
var api = Spark.getGameDataService();
//Create a new game data object
var playerObject = api.createItem("Player", playerId);
//Get the data from the object
var playerData = playerObject.getData();
//Adding some values to the data
playerData.weapon = "Spoon";
playerData.strength = 5;
//Saving data
var docStatus = playerObject.persistor().persist().error();
// Check if documents saved
if (docStatus) {
   // return error if peristence iterrupted
   Spark.setScriptError("error", docStatus);
}

 Loading and editing existing data

var playerId = Spark.getPlayer().getPlayerId();
//Retrieve gaemspark data service
var api = Spark.getGameDataService();
//Get existing data object
var playerObject = api.getItem("Player", playerId);
//Get the data from the object (Take not that when retrieving you need to add .document())
var playerData = playerObject.document().getData();
//Adding some values to the data
playerData.weapon = "Fork";
playerData.strength = 10;
//Saving data (Same with adding .document() here also)
var docStatus = playerObject.document().persistor().persist().error();
// Check if documents saved
if (docStatus) {
   // return error if peristence iterrupted
   Spark.setScriptError("error", docStatus);
}

 If you were to only edit say weapon in this, it would only update weapon and keep the rest as it was.


I believe if you are storing all the data in the player data you would have to load that data into a variable, change it and then save it. I also think data like this is better off being stored using game data, you could have different data for each class hooked up to the player I'd, retrieve the data when needed and update the current values.

Thanks for the reply chase,


I've read over the gamedata so many times but i have absolutely no idea how to use it. I'm coding my game in unity and all i'd like to do is store some character stats and then update them and be able to load them when i start the game.


Could you / anyone please provide an extremely basic example of creating the data service, storing 2 variables, updating and then loading those 2 variables.


There's very complex tutorials on how to do things but i can't find just basic saving and recalling in cloud code. I've gone back and forth for months between privatedata and game data and just need some guidance.


Thanks,

Ryan

Answer

Hey Ryan,


The basics of it are quite easy once you get used to it, here are some examples. 


Creating new GameData for 'Player'

var playerId = Spark.getPlayer().getPlayerId();
//Retrieve gaemspark data service
var api = Spark.getGameDataService();
//Create a new game data object
var playerObject = api.createItem("Player", playerId);
//Get the data from the object
var playerData = playerObject.getData();
//Adding some values to the data
playerData.weapon = "Spoon";
playerData.strength = 5;
//Saving data
var docStatus = playerObject.persistor().persist().error();
// Check if documents saved
if (docStatus) {
   // return error if peristence iterrupted
   Spark.setScriptError("error", docStatus);
}

 Loading and editing existing data

var playerId = Spark.getPlayer().getPlayerId();
//Retrieve gaemspark data service
var api = Spark.getGameDataService();
//Get existing data object
var playerObject = api.getItem("Player", playerId);
//Get the data from the object (Take not that when retrieving you need to add .document())
var playerData = playerObject.document().getData();
//Adding some values to the data
playerData.weapon = "Fork";
playerData.strength = 10;
//Saving data (Same with adding .document() here also)
var docStatus = playerObject.document().persistor().persist().error();
// Check if documents saved
if (docStatus) {
   // return error if peristence iterrupted
   Spark.setScriptError("error", docStatus);
}

 If you were to only edit say weapon in this, it would only update weapon and keep the rest as it was.


1 person likes this

Ahhhhh!!!!!!!!!!


This is so much of what i've needed.


I have my data now saving and i can return it. I'm able to update it in parts, thank you so much!


There's one question i'm left with at the moment which is bit more complex.


Below is my current script to create the player's ID for data:


 

if(!Spark.hasScriptErrors()){

//Load API

var API = Spark.getGameDataService();



//get player ID and the class name of the active class.

var playerId = Spark.getPlayer().getPlayerId();

var CLASSNAME = Spark.getData().ClassName;



var WEAPON_LV = Spark.getData().WEAPON_LV;

var ARMOR_LV = Spark.getData().ARMOR_LV;

var WEAPON_LV = Spark.getData().HELMET_LV;

var WEAPON_LV = Spark.getData().SHIELD_LV;

//Create the gamedata table called PlayerInformation and add in the person's PlayerID as the ID of the user.

var characterObject = API.createItem("PlayerInformation", playerId);



//Get Data that exists in that ID

var characterData = characterObject.getData();



characterData.WEAPON_LV = WEAPON_LV;

characterData.ARMOR_LV = Spark.getData().ARMOR_LV;

characterData.HELMET_LV = Spark.getData().HELMET_LV;

characterData.SHIELD_LV = Spark.getData().SHIELD_LV;



var docStatus = characterObject.persistor().persist().error();



if(docStatus){

    //return error if persistence interrupted

    Spark.setScriptError("ERROR", docStatus)

}

}

 What i would like to achieve, is a file path that goes through the ClassName then weapon_lv, i can then dynamically set ClassName to something like Warrior for example and i would have a characterData.Warrior.WEAPON_LV


When i try to run this with the line


characterData.CLASSNAME.WEAPON_LV = WEAPON_LV;


I end up with an error that tells me that it "Cannot set property "WEAPON_LV" of undefined to "1":


image


I would like to dynamically set where this goes in as the ClassName in Unity3d will be the class name of my current main character.


That way when im playing a Warrior, i update into the warrior's weaponlv and when i play a mage i update into the mage's weaponlv through the same piece of code.


Once again, thank you so much for your help :)


Ryan

Ok, so the reason behind this is that you are trying to access the literal path of 'characterData.CLASSNAME.WEAPON_LV'.

To access something through a variable you use: 

  

var CLASSNAME = "Wizard";
'characterData[CLASSNAME].WEAPON_LV' = 5;

 


On top of that, if characterData.Wizard doesnt exist already its going to be undefined, so if you arent setting all the default at account creation you would have to add a check before this, something like 

if(!characterData[CLASSNAME]){
    characterData[CLASSNAME] = {};
}

  


What this does is add another javascript object to the object you are using so that you can add keyValue pairs to it.

I have so many questions about more things, but you've answered this thread thoroughly, i appreciate your time and thanks again :)


You've saved me what has been literal hours of trying to make sense of this.


Ryan

Not a problem :) It took me a while to learn some core features of GameSparks so I completely understand where you are coming from. 


If I see any other questions around I'll see what I can help with.

Hey Chase,


I have one last question - apologies for keeping this thread open.


I have the full back end for this working correctly as i want it to now. I'm working in Unity3d and i am able to effectively save the data that i want, but i'm not sure what the Syntax would be to load my character.


This is what i would be using currently as my cloud code for a 'load' script   

var playerId = Spark.getPlayer().getPlayerId();
//Retrieve gamespark data service
var api = Spark.getGameDataService();
//get my class name so i know what i will be searching for
var CLASSNAME = Spark.getData().ClassName;
//Get existing data object
var characterObject = api.getItem("PlayerInformation", playerId);


var characterData = characterObject.document().getData();
 

  I can see that adding the .document().getData() gives me the breakdown of my data, however i'm not sure how i actually go about accessing and saving those fields as variables in unity.


If i have a look at the tutorial example it suggests code like:


 

new GameSparks.Api.Requests.LogEventRequest().SetEventKey("LOAD_PLAYER").Send((response) => {
    if (!response.HasErrors) {
        Debug.Log("Received Player Data From GameSparks...");
        GSData data = response.ScriptData.GetGSData("player_Data");
        print("Player ID: " + data.GetString("playerID"));
        print("Player XP: " + data.GetString("playerXP"));
        print("Player Gold: " + data.GetString("playerGold"));
        print("Player Pos: " + data.GetString("playerPos"));
    } else {
        Debug.Log("Error Loading Player Data...");
    }
});

 However this uses scriptdata so i can't imagine that the syntax is the same. I've tried to find a unity equivalent, even just looking through the intellisense in visual studio but i haven't come up with much.


Do you know how i would load data from these data types into my game?


Cheers,

Ryan

Hey Ryan,


The way you would get this to the client is by using scriptData, use something like:

var playerId = Spark.getPlayer().getPlayerId();
//Retrieve gamespark data service
var api = Spark.getGameDataService();
//get my class name so i know what i will be searching for
var CLASSNAME = Spark.getData().ClassName;
//Get existing data object
var characterObject = api.getItem("PlayerInformation", playerId);


var characterData = characterObject.document().getData();
 
Spark.setScriptData("characterData", characterData);

 The way you would get this inside of unity is the same as you have shown above, each object in the script data is classed as GSData, the other types are pretty self explanatory.
You could do something like: 

new GameSparks.Api.Requests.LogEventRequest().SetEventKey("LOAD_PLAYER").Send((response) => {
    if (response.HasErrors) {
        Debug.Log(response.Errors.JSON);
	}else{
        Debug.Log("Received Player Data From GameSparks...");
        GSData data = response.ScriptData.GetGSData("characterData");
		
		GSData warriorData = data.GetGSData("WARRIOR");
		int warriorWeaponLevel = warriorData.GetString("WEAPON_LVL");
		int warriorArmorLevel = warriorData.GetString("ARMOR_LV");
		int warriorHelmetLevel = warriorData.GetString("HELMET_LV");
		int warriorShieldLevel = warriorData.GetString("SHIELD_LV");
		
		GSData wizardData = data.GetGSData("WIZARD");
		int wizardWeaponLevel = warriorData.GetString("WEAPON_LVL");
		int wizardArmorLevel = warriorData.GetString("ARMOR_LV");
		int wizardHelmetLevel = warriorData.GetString("HELMET_LV");
		int wizardShieldLevel = warriorData.GetString("SHIELD_LV");
    }
});

  This would obviously get those values for each class and store them in the variables, however you would definitely be better off making a Dictionary to store the variables, this way you could loop through the data without having to hard code each class and repeat code every time.

Something like: 

Dictionary<string, int> weaponLevels = new Dictionary<string, int>();
Dictionary<string, int> armorLevels = new Dictionary<string, int>();
Dictionary<string, int> helmetLevels = new Dictionary<string, int>();
Dictionary<string, int> shieldLevels = new Dictionary<string, int>();

new GameSparks.Api.Requests.LogEventRequest().SetEventKey("LOAD_PLAYER").Send((response) => {
    if (response.HasErrors) {
        Debug.Log(response.Errors.JSON);
	}else{
        Debug.Log("Received Player Data From GameSparks...");
        GSData data = response.ScriptData.GetGSData("characterData");
		
		foreach(KeyValuePair(string, object) class in data.BaseData){
			GSData classData = class.Value as GSData;
			weaponLevels.Add(class.Key, classData.GetString("WEAPON_LVL");
			armorLevels.Add(class.Key, classData.GetString("ARMOR_LV");
			helmetLevels.Add(class.Key, classData.GetString("HELMET_LV");
			shieldLevels.Add(class.Key, classData.GetString("SHIELD_LV");
		}
    }
});

 Even better again you should be making a class or struct to store the class data in, then you can simply make a dictionary of that struct/class to store all the data in 1 dictionary.

Hi Chase,


Thanks again for the quick reply. I've added in the 'Spark.getPlayer().setScriptData("characterData", characterData); '


to set it to script data, this works correctly:

image


However when trying to recall this, i'm getting a null exception error:


image




the code i'm using for this is as below:


  

        public void LoadEntireCharacter()
        {
            new GameSparks.Api.Requests.LogEventRequest().SetEventKey("LOAD_PLAYER_STATS").Send((response) => {
                if (response.HasErrors)
                {
                    Debug.Log(response.Errors.JSON);
                }
                else
                {
                    Debug.Log("Received Player Data From GameSparks...");
                    GSData data = response.ScriptData.GetGSData("characterData");
                    GSData warriorData = data.GetGSData("Warrior");
                    Debug.Log(warriorData.GetString("WEAPON_LV"));
                    Debug.Log("It never makes it to this point");

  

It never makes it to the second debug.log, it's almost as if it can't find the characterData, but as far as i can tell that is correct and does exist, is there a different way that i'm supposed to be referencing the warrior array in the GS data?


I've tried using 

GSData data = response.ScriptData.GetGSData("characterData").GetGSData("Warrior"); but it seems to just throw the same exception that way too.


I initially thought i would have to reference all of the data before it would let me so i've also tried setting all of the variables to data but i still get the same error.


It looks like it doesn't recognize the warriorData.GetInt() as an int either and gives me syntax errors so i've done it as below:


mainForgeWeapon = int.Parse(warriorData.GetString("WEAPON_LV"), System.Globalization.NumberStyles.Integer); 


I haven't used dictionaries before so i'm going to do it the long way for now and improve it down the track.


Apologies for what is most likely straight forward questions, but in relation to unity they only have the one page which has the code that we've linked earlier in this thread.


Kind Regards,

Ryan

Hey Ryan,


I believe this is due to they type coming back, warriorData.GetInt("WEAPON_LVL") will return an 'int?', the exception you get for not type casting should be a type error, so you need to cast this to a regular int.


This can be done by calling 

(int)warrirData.GetInt("WEAPON_LEVEL");

GetString will return a null reference because it is not a string :)


When you come across something like this I always find it is a good idea to check the parent with something like this: 

if(characterData == null){
    Debug.Log("characterData null"
}else{
    Debug.Log(characterData.JSONString);
}

 

Hey mate,

I get where you're coming from but no luck on my side.


It looks like it doesn't get past the line:

GSData characterData = response.ScriptData.GetGSData("characterData");



I've changed the query to run:


 

        public void LoadEntireCharacter()
        {
            new GameSparks.Api.Requests.LogEventRequest().SetEventKey("LOAD_PLAYER_STATS").Send((response) => {
                if (response.HasErrors)
                {
                    Debug.Log(response.Errors.JSON);
                }
                else
                {
                    Debug.Log("Received Player Data From GameSparks...");
                    GSData characterData = response.ScriptData.GetGSData("characterData");
                    Debug.Log("Never Gets to here");
                }
            });
        }

 Is this definitely the correct way to reference the characterData?


I'm pretty certain that from the cloud side we have everything correct now otherwise the data wouldn't be sitting inside of the script data:


image



but it doesn't seem to be finding this when we run the query. I've deleted every other ID out, including this one and re-created them through the client, It would definitely be pulling the data from this as the device auth registers them on the first login.


I've attempted to add in the :

if(characterData == null){ Debug.Log("characterData null" }else{ Debug.Log(characterData.JSONString); }


but nothing will get it past the line beforehand. also i think the characterData.JSONString is not accepted by unity, but that's not really a problem.


i've also tried with adding: mainForgeWeapon = (int)warriorData.GetInt("WEAPON_LV");


but i'm certain it's just failing to get the data correct even though the initial request is being received correctly.


Looking through the syntax nothing else really makes sense unless it refers to it as GetGSDataList?


Kind Regards,

Ryan

Hey, Looking at that it looks like script data is being incorrectly set. You are currently setting it to the players script data which is in the player record rather than the script data that is returned from an event request. To do this change Spark.getPlayer().setScriptData(); to Spark.setScriptData(); This should fix this problem :) There is a function on gsdata that will show json data in the debug also, I thought I was JSONString, but if it's not there's something there, not on pc right now ha.

Heeeey!


That did it, thank you very much for your help :)


I think i can finally step away and set up the front end properly now.


I hopefully won't have to touch this again until i do chats hahah.


I've definitely been inspired to start helping others when i get a bit better at this though, i truly appreciate your help :)


Thanks,

Ryan


1 person likes this
Login to post a comment