Sign In Register

How can we help you today?

Start a new topic
Answered

save event with array in Unity

Hello.

I'm a complete noob in GS, so forgive me if my question might sound too simple.

I've followed the tutorial on how to store a ghost race replay and it works fine. This is exactly what I need. Unfortunately, the tutorial stops right before the last step: save the data (and load it) from inside a C# script.


Browsing the web I found something and I could make this, that works neatly:


 

		LogEventRequest request = new LogEventRequest();
		request.SetEventKey ("STORE_RACE_DATA");
		request.SetEventAttribute ("GHOST_DATA", jsonString);
		request.SetEventAttribute ("TRACK_NUMBER", DataController.dataController.currentRaceData.GetLevelNumber());
		request.SetEventAttribute ("RACE_TIME", Round1000 (DataController.dataController.currentRaceData.GetFinalTime()));
		request.SetEventAttribute ("STARSHIP_NUMBER", DataController.dataController.data.GetActiveStarhip());

		request.Send ((response) => {
			if (!response.HasErrors)
				Debug.Log ("added");
			else
				Debug.Log ("error " + response.Errors.JSON);
		});

The data structure I have is very similar to the one of the tutorial:

TRACK_NUMBER, RACE_TIME, STARSHIP_NUMBER are Numbers, while GHOST_DATA is of type JSON.

Here is where I have the trouble: GHOST_DATA is an array and if I create the Json string it is interpreted as a simple string. How could I save the array in GHOST_DATA?


I hope I was clear, thanks for any possible future help.



Best Answer

 Hey Beppi,

Running your 'GRD' event in the test harness works fine for me and im getting responses back without any problems.

I'm guessing you are using unity, so i've checked the call there too, and the data is coming back in the response there too (but it is very temperamental with that encoded string because some of them are very long).
--> See first Pic

So, i assume you are having problem on the 'unity' end getting the data out?
I have a solution for you to try, but that ghostData element is not going to play nice unless you have something special on your side to parse it, its just too long

 

new GameSparks.Api.Requests.LogEventRequest()
			.SetEventKey("GRD")
				.SetEventAttribute("TN", "0")
					.SetEventAttribute("ST", "3")
						.SetDurable(true)
							.Send((response) => {

					// get all the objects in the response script data, this is each race that met the event attributes you requested //
					// When you sent the scriptdata back in CloudCode, you named it "selectedData", so this helps us get specifically that data//
					List<object>  dataList = response.ScriptData.GetObjectList("selectedData"); 
					// now we can iterate through each element as if it is a dictionary, just use the key and get each item //
					foreach (Dictionary<string, object> raceData in dataList) 
					{
						// each of these are converted to a string since we dont know exactly whats in each element at this stage //
						string nickName = raceData["nickName"].ToString();
						int trackNo = int.Parse(raceData["trackNumber"].ToString());
						int starShipNumber = int.Parse(raceData["starshipNumber"].ToString());
						int timeTaken = int.Parse(raceData["timeTaken"].ToString());
						long raceDate = long.Parse(raceData["raceDate"].ToString()); // need to check the date-format here so it can be parsed //
						string playerID = raceData["playerId"].ToString();

						object ghostData = raceData["ghostData"]; // just save this as a object for now

						// Just print these 
						print("Nick Name: "+nickName);
						print ("Track Number: "+trackNo);
						print ("Ship Number: "+starShipNumber);
						print ("Time Taken "+timeTaken+"s");
						print ("Date: "+raceDate);
						print ("Player ID: "+playerID);
					}

				});

 

You can see the result of this call in the second picture, along with the json-data received in the response.

I hope that helps?
If you still need help adding the array data instead of the encrypted string, please let me know.

Sean
Untitled2.png
(90.5 KB)
Untitled.png
(69.5 KB)

Yes, you'll defiantly need the player's ID if you want load their details and get their facebook-ID. Could you get around this by loading the player-ID into the race-data?
If i understand it right, a player runs the race and the details of the race are stored in your raceData collection; then a challenger can attempt to beat the original racer? If thats is the way it works storing a list of playerIDs who have challenged each race (along with the 'show-facebook-picture' bool), will allow you to grab a list of all the race data and get the player's fb-ID from that data. You could even have the fb-ID and the playerID stored in the race-data collection. Does that sound right?

To get the facebook id attached to the script you can just send it as a string...

var playerDetails = Spark.loadPlayer(playerID);
var fbID = playerDetails.getExternalIds();
Spark.setScriptData("fb_ID", fbID);

this will come out as a json string something like this ...

 

{{"fb_ID"} :{"FB":"1111111111111"}}

 

Then you can get the exact string out in your Unity code...

 

if(!response.HasErrors)
					{
						GSData list = response.ScriptData.GetObject("fb_ID");
						string fbID = list.GetString("FB");
					}

 

From there you could call the FB SDK with that id to get the profile pic.
I remember its not very difficult to get, but its been a while since i did it myself.
Theres some good advice here -> http://stackoverflow.com/questions/19756453/how-to-get-users-profile-picture-with-facebooks-unity-sdk

 

That's what I tried to do but loadPlayer seems to work only if the opponent player is a user's friend, and that's not the case when a challenge is organized. I need to access to Spark.loadPlayer for a playerId that is not known. For that reason I'm looking for a way to get the picture anyway going round this (wanted) limitation.

Also, supposing I have the fbId, how do I attach it to the ScriptData?

Anyway, I will go through my duplicate-fbId solution.


So, if the player logged into Gamesparks through facebook you can get their facebook ID.
If is stored in their player data, so you will need their playerID (their gamesparks one)...

var facebookID

 

var playerDetails = Spark.loadPlayer(playerID);
var facebookID = playerDetails.getExternalIds();

That will get you a string looking something like "FB" : "122308734598734" , for example.
So you can get the facebook ID that way. Then if you want you could send the fb-ID back in the response and use the Facebook SDK in unity to get the profile picture from that ID.

Does that help?

 

I cannot contact you by Skype.

The races are asynchronous, the player actually races against a replay. Every user saves his replay and the replay is retrieved by other users. This guarantees that there are always opponents. If the critical user base will increase enough after the first app launch, I will create a real time challenge system.


What I would like to retrieve is the picture of the opponent that recorded the replay, for which I know the playerId.


I see two ways: I can create a local binary file and let the user upload the binary image himself to a public document accessible by future opponents, or store the fbid of the player so that it can retrieved by any user and the facebook picture taken by internet. What's the best solution in your opinion?


In the first case, I don't know how to upload a unique binary file for every user, going in update mode everytime it changes (without adding new files every time).


The second case it's not what I like. I don't like to duplicate information that is somewhere else, but if this is the solution, it's not hard to implement it. I would prefer a way to access directly to the opponent's user information and get his FBid without replicating it in a public document.

Yes, thats correct.
So it sounds like we need to sort out a way to update the race-data with the id of any player who enters?
Is the playerID you store in the raceData the opponent's id or the current user's id?

How do you link two users into a race?
It would be simplest to add user data into the raceData when they join, then you can access it afterwards.


 

Here I am Sean.

I think I understood one thing, tell me if it's correct. I cannot access opponents Data because I access as user that is not a opponent's friend. Is it correct?

Anyway basically what I want is to be able to retrieve the playerId from the replay (that is opponent's id) and send to the client these information:

- boolean if the user wants to display the picture

- facebook picture or facebook id


Please tell me if you prefer to use a different media to talk as soon as the forum might be too slow. Thank you.

Save the head-banging for the mosh-pit Beppi, i'll take a look at this for you now


-Sean

I just wanted to say that I don't feel like opening a ticket just because your service works wonderfully, it's just me, not you :D :D

Dear Jamie,

apologies are mine for having been maybe too urgent in my requests, no problem.

Sean has been very clear and kind, making all his efforts to help me. Thank him from me. And you are too.

Anyway I think that the major problem is not in your assistance that obviously depends from office hours, but on documentation, that is always on no matter whata you do, due to the fact that the often people (like me) that come to this service are not experts of mongodb, javascript and all the technology that you created. But unfortunately documentation looks like it has written by people that already know so many things around that are not necessarily clear.


For example: I've been banging my head for hours now. I want to retrieve a single element by a single cursor derived from a single query on my mongodb, and use it in another query to return additional information. The idea is to send to the user that requested a race replay, also the fbId of his opponent and if the opponents wants his picture to be displayed.

Probably it's a trivial operation, but I've been here since hours. In sql I would have written it in 1 line of code. My fault, and I'm now studying mongodb, but Gamesparks unfortunately doesn't help in this process, the training curve is sloooow: people could abandon before the fight, not me.

These are the questions that arose:

- how do I get a single value by a cursor. I want to get just the "playerId" value from the raceData I received. I did it but I keep on receiving "null" errors from javascript. The execution of 

var oppId = selectedData.curr().get("playerId");

 fails saying selectedData.curr() is null.


- I don't understand, once I will have the oppId that I'm desperately trying to get, if I am allowed to do something like this: 

Spark.setScriptData("selectedData", selectedData);
Spark.setScriptData("oppId", oppId);

 and receive both the information on the client side.


- I didn't undersand how to have the Cloud Code write on a console, so that I can fastly test locally.


I'm sure that all this information is available, or just obvious, unfortunately I'm still banging my head... :((


Thank you


Hi Beppi,

Apologies for the delay in getting your answer to you, we know it can be frustrating trying to find a solution to a problem especially if it takes a few days.

I just thought I would pop in and say that our customer support team operate from Monday to Friday 9am - 5:30pm UTC+01:00 so they would not have seen your post until yesterday morning. The customer support team during these hours will be browsing the forums trying to help in any way they can.

For certain issues it is recommended that you open a support ticket here, especially if it is an urgent issue. If it is an emergency then it is escalated to tech support who are notified even on weekends (note these are rare cases like outages or critical errors etc). The goal for the forums is to create a community where developers help other developers which a lot of people have been doing but we still help out like we have done here.

Apologies again for the delay again but it is worth noting that outside of office hours it will be rare to get an answer in our forums from customer support and even a lot of our community may not work on weekends.

Hope this helps,
Kind Regards,
Jamie


 

It is sent over websockets as plaintext, but the limit is around 130k for the response, so you should be okay.

 

Thank you very much, I'll check your solution, anyway. The string is very long because it contains an encoded replay. At a first glance I set it as an array, like in the tutorial https://docs.gamesparks.net/howtos/cloud-data/how-to-implement-data-sharing.

then I was not able to retrieve that complex JSON object (although now, after all this work, probably I would be able), and turned on an encoded string, that BTW takes about 1/5 of the space of a JSON array. For that reason I was asking if the transfer on the network is compressed and binary: 40KB can be a problem on some low bandwidth mobile device.

Sorry Beppi, i didn't see that last post in time.

Glad to see you got it working.
There isn't really a 'right' way to get the data out, it really depends on what you are most comfortable with.
I just like to use the GetObjectList() method because it is super-handy to iterate through a dictionary to get the elements i need.

Sean

 

Answer

 Hey Beppi,

Running your 'GRD' event in the test harness works fine for me and im getting responses back without any problems.

I'm guessing you are using unity, so i've checked the call there too, and the data is coming back in the response there too (but it is very temperamental with that encoded string because some of them are very long).
--> See first Pic

So, i assume you are having problem on the 'unity' end getting the data out?
I have a solution for you to try, but that ghostData element is not going to play nice unless you have something special on your side to parse it, its just too long

 

new GameSparks.Api.Requests.LogEventRequest()
			.SetEventKey("GRD")
				.SetEventAttribute("TN", "0")
					.SetEventAttribute("ST", "3")
						.SetDurable(true)
							.Send((response) => {

					// get all the objects in the response script data, this is each race that met the event attributes you requested //
					// When you sent the scriptdata back in CloudCode, you named it "selectedData", so this helps us get specifically that data//
					List<object>  dataList = response.ScriptData.GetObjectList("selectedData"); 
					// now we can iterate through each element as if it is a dictionary, just use the key and get each item //
					foreach (Dictionary<string, object> raceData in dataList) 
					{
						// each of these are converted to a string since we dont know exactly whats in each element at this stage //
						string nickName = raceData["nickName"].ToString();
						int trackNo = int.Parse(raceData["trackNumber"].ToString());
						int starShipNumber = int.Parse(raceData["starshipNumber"].ToString());
						int timeTaken = int.Parse(raceData["timeTaken"].ToString());
						long raceDate = long.Parse(raceData["raceDate"].ToString()); // need to check the date-format here so it can be parsed //
						string playerID = raceData["playerId"].ToString();

						object ghostData = raceData["ghostData"]; // just save this as a object for now

						// Just print these 
						print("Nick Name: "+nickName);
						print ("Track Number: "+trackNo);
						print ("Ship Number: "+starShipNumber);
						print ("Time Taken "+timeTaken+"s");
						print ("Date: "+raceDate);
						print ("Player ID: "+playerID);
					}

				});

 

You can see the result of this call in the second picture, along with the json-data received in the response.

I hope that helps?
If you still need help adding the array data instead of the encrypted string, please let me know.

Sean
Untitled2.png
(90.5 KB)
Untitled.png
(69.5 KB)

Thank you very much,


I finally succeeded in getting the data with this, is this the correct way?

 

	public void LoadReplayFromServer() {  // TODO
		Debug.Log("Load Replay From Server");

		string raceData, nickName;
		int? starshipNumber, timeTaken;

		LogEventRequest request = new LogEventRequest();
		request.SetEventKey ("GRD")
				.SetEventAttribute ("TN", DataController.dataController.currentRaceData.GetLevelNumber())
				.SetEventAttribute ("ST", Constants.constants.starshipCharacteristics[DataController.dataController.currentRaceData.starshipType].GetTier());

		request.Send ((response) => {
			if (response.HasErrors) {
				Debug.Log("FAIL with error " + response.Errors.JSON);
			} else {
				string json = response.ScriptData.JSON;
				Debug.Log (json);
				var N = SimpleJSON.JSON.Parse(json);

				raceData = N["selectedData"][0]["ghostData"].Value;
				nickName = N["selectedData"][0]["nickName"].Value;
				starshipNumber = N["selectedData"][0]["starshipNumber"].AsInt;
				timeTaken = N["selectedData"][0]["timeTaken"].AsInt;
			}
		});
	}

 Using this utility:

http://wiki.unity3d.com/index.php/SimpleJSON


I got no success in any way using GetString() and other methods that wouldn't force me to pass through a JSON.


How is the data transmission performed? does it transfer a JSON or does it transfer binary compressed data and then transformed in JSON on client-side?

Thanks.

Login to post a comment