create token in unity to send post request to laravel controller - unity3d

I wanna send a post request from a unity game to a laravel 5.4 controller.. in html form, we use {{csrf_field}} and it handles creating token. but how can I do it in unity?

I came across this thread whilst trying to acheive what it set out to do - and I got it working so I am sharing this:
It is a combination of this tutorial on YouTube: How to Use UnityWebRequest - Replacement for WWW Class - Unity Tutorial and this answer on a similar question: UnityWebRequest POST to PHP not work
The technology I am using:
Laravel Framework 7.15.0 (PHP)
Unity: 2019.3.11f1 (script is C#)
Now I am just sending basic forms not images etc - so I haven't tried anything complex but this sends a get to one URL on Laravel which returns the CSRF token.
This is then added as an additional field to the post request.
I am clicking the 2 buttons pretty quickly - I would suggest (and will implement this myself later) that you don't ask for the token until your ready to submit the form - to avoid it expiring if your form is quite long etc.
The C# Script (Unity)
Both the functions are set as ON Click() on the buttons in a simple UI Canvas in Unity.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class BasicWebCall : MonoBehaviour
{
public Text messageText;
public InputField scoreToSend;
private string csrf_token;
readonly string getURL = "http://mazegames.test/leaderboard/1";
readonly string postURL = "http://mazegames.test/register/1";
private void Start()
{
messageText.text = "Press buttons to interact with web server";
}
public void OnButtonGetScore()
{
messageText.text = "Getting Token";
StartCoroutine(SimpleGetRequest());
}
IEnumerator SimpleGetRequest()
{
UnityWebRequest www = UnityWebRequest.Get(getURL);
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError)
{
Debug.LogError(www.error);
}
else
{
messageText.text = "Token Received: " + www.downloadHandler.text;
csrf_token = www.downloadHandler.text;
}
}
public void OnButtonSendScore()
{
if (scoreToSend.text == string.Empty)
{
messageText.text = "Error: No high score to send.\nEnter a value in the input field.";
}
else
{
messageText.text = "Sending Post Request";
StartCoroutine(SimplePostRequest(scoreToSend.text));
}
}
IEnumerator SimplePostRequest(string curScore)
{
Dictionary<string, string> wwwForm = new Dictionary<string, string>();
wwwForm.Add("_token", csrf_token);
UnityWebRequest www = UnityWebRequest.Post(postURL, wwwForm);
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.LogError(www.error);
}
else
{
messageText.text = www.downloadHandler.text;
}
}
}
Laravel routes/web.php
Obviously you need to configure your end points to receive your data, validate and store it if necessary. In this example its just validating the token as part of the post request.
Route::post('register/{game_id}', function($game_id) {
return "Game On!";
});
Route::get('leaderboard/{game_id}', function($game_id) {
return csrf_token();
});
And that is pretty much it - hope this helps someone else.
#EDIT# - Request Token on Submission
To only get the token when your submitting the form, literally all you have to do is put this line:
StartCoroutine(SimpleGetRequest());
above the line:
StartCoroutine(SimplePostRequest(scoreToSend.text));
so that is looks like this:
StartCoroutine(SimpleGetRequest());
StartCoroutine(SimplePostRequest(scoreToSend.text));
Obviously you could then remove the function SimpleGetRequest altogether.

Laravel will generate a token each time a page is generated. The token has a lifetime and after that lifetime it cannot be used anymore (that's the whole point).
You need to get a valid token from Laravel pass it to Unity3D and then when from Unity create a WWWForm and pass it back.
How to do this it depends on the platform that Unity3D is deployed to.
If you are using WebPlayer or WebGL then you can get your hand on the Unity3D objected embedded in the browser and use SendMessage. WebGL link here.
If the game is deployed to another platform it probably makes sense to expose and API on the Laravel side and use that endpoint instead of doing a POST request.

You can use WWWForm to send in POST and call it from a coroutine:
// this will send it at start
// but you can just call SendToController in another function
string laravel_url = "http://somedomain.com/whatever";
IEnumerator Start () {
yield return StartCoroutine(SendToController());
}
IEnumerator SendToController()
{
WWWForm form = new WWWForm();
form.AddField( "csrf_field", "replace this with what you want!!!!" );
WWW download = new WWW( laravel_url, form );
yield return download;
if(!string.IsNullOrEmpty(download.error)) {
print( "Error downloading: " + download.error );
} else {
// if succesful, do what you want
Debug.Log(download.text);
}
}
"Coroutine" is your friend. It will make sending forms a lot easier. You might want to read about it in here:
https://docs.unity3d.com/ScriptReference/MonoBehaviour.StartCoroutine.html

Related

UnityWebRequest take 7ko for send 2 numbers how optimize this?

hi everyone i need help to optimize a web request with Unity.
I need to send two numbers to my inline php script. I use this code
IEnumerator request()
{
WWWForm form = new WWWForm();
form.AddField("id", "12345");
form.AddField("hp", "9999");
using (UnityWebRequest www = UnityWebRequest.Post("https://website.com/script.php", form))
{
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
}
else
{
mytext.text = www.downloadHandler.text;
}
}
}
But each time I call this request, I use 6.5kb of internet. My application calls this query every 10 seconds. This application is used for training outside, so the user is probably using their lte network to play.
I need to reduce this cost as much as possible, how can I do that?

Runnable.Run / StartCoroutine calles to Watson services from Unity

In my ExampleStreaming.cs script, once the user utterance is recognized as final, I send it to both the Watson Assistant service and the Tone Analyzer. Because I am keeping the scripts for each service separate as they are, I have to make calls within each script to access the other service. You can see the call I make to the Tone Analyzer below (the .SendToneAnalysis method):
private void OnRecognize(SpeechRecognitionEvent result, Dictionary<string, object> customData)
{
blah blah blah . . .
/// Only send the recognized speech utterance to the
/// Assistant once we know the user has stopped talking.
if (res.final)
{
string _conversationString = alt.transcript;
Runnable.Run( StopRecording(1f) ); // Stop the microphone from listening.
/// Message.
Dictionary<string, object> input = new Dictionary<string, object>
{
["text"] = _conversationString
};
MessageRequest messageRequest = new MessageRequest()
{
Input = input,
Context = _Context
};
_exampleAssistantV1_script.SendMessageAssistant(messageRequest);
_exampleToneAnalyzer.SendToneAnalysis(_conversationString);
. . .
In my ExampleToneAnalyzer.cs script, I make a simple call to the event-handling methods that are meant to contact the service and also handle success & failure:
public void SendToneAnalysis(string conversationString)
{
_service.GetToneAnalyze(OnGetToneAnalyze, OnFail, conversationString);
}
These calls are typically made using StartCoroutines, particularly in the Watson Unity SDK that there is a specialized Runnable.Run which is essentially a helper class for running co-routines without having to inherit from MonoBehavior.
My question is whether my simple method call to the service might be problematic in certain situations or perhaps just wrong or bad programming, or whether it is perfectly OK to go for that method instead of something like the following:
public void SendToneAnalysis(string conversationString)
{
Runnable.Run( SendAssistantToneAnalysis(conversationString) );
}
private IEnumerator SendAssistantToneAnalysis(string conversationString)
{
if ( !_service.GetToneAnalyze(OnGetToneAnalyze, OnFail, conversationString) )
{
Log.Debug("ExampleToneAnalyzer.SendAssistantToneAnalysis()", "Failed to analyze!");
}
while (!_UserUtteranceToneTested)
yield return null;
}
You don't need to make any of the service calls from within a coroutine. Only authentication using iamApikey should be done using a coroutine
IEnumerator TokenExample()
{
// Create IAM token options and supply the apikey. IamUrl is the URL used to get the
// authorization token using the IamApiKey. It defaults to https://iam.bluemix.net/identity/token
TokenOptions iamTokenOptions = new TokenOptions()
{
IamApiKey = "<iam-api-key>",
IamUrl = "<iam-url>"
};
// Create credentials using the IAM token options
_credentials = new Credentials(iamTokenOptions, "<service-url>");
while (!_credentials.HasIamTokenData())
yield return null;
_assistant = new Assistant(_credentials);
_assistant.VersionDate = "2018-02-16";
_assistant.ListWorkspaces(OnListWorkspaces, OnFail);
}
The examples are only meant to show how to invoke the service call. The only reason the code is invoked from a coroutine is so we can wait for the response of one service call before running another service call (i.e. so we don't try to update or delete a workspace before the workspace is created).
It's no problem.
Runnable.Run() eventually calls StartCoroutine() as the follow.
public Routine(IEnumerator a_enumerator)
{
_enumerator = a_enumerator;
Runnable.Instance.StartCoroutine(this);
Stop = false;
ID = Runnable.Instance._nextRoutineId++;
Runnable.Instance._routines[ID] = this;
#if ENABLE_RUNNABLE_DEBUGGING
Log.Debug("Runnable.Routine()", "Coroutine {0} started.", ID );
#endif
}
Please refer to https://github.com/watson-developer-cloud/unity-sdk/blob/master/Scripts/Utilities/Runnable.cs
And the coroutine can be called from any gameobject, if it is active.

Callback function issues in FB.API for Unity

I am using Unity 5.5.2f1 pro and facebook's SDK v 7.9.4
I have a script which after login (managed in a previous scene) sends an API request to FB asking for friends, name and email and sends that info as a POST to a php website.
code:
[Serializable]
public struct FBData {
public string first_name;
public string email;
public string friends;
public string id;}
public class UserManagement : MonoBehaviour {
string urlSaveUserData="some php website";
public Text testTxt;
FBData parsedData;
// Use this for initialization
void Start () {
//Check if it's the first time the user is opening the app.
if (UserInfo.FIRST_TIME) {
//update text (only used for testing, should be removed in production.)
testTxt.text = "Your user id is: " + UserInfo.ID;
//Perform FB.API call to get User Data.
getUserData ();
//Save in SQL table. (won't get here if line in getUserData() is active)
StartCoroutine ("saveUserData");
} else {
//do something else.
}
note: Since this is meant for iOS I have to test it on a device so I'm using text in the screen to display info (think of it as a badly implemented print statement).
The problem: In my callback function for FB.API I write in the text Gameobject (aka testTxt) the parsed information from the response which is saved in the Custom UserInfo clss. It display's correctly but the code gets stuck there. It doesn't continue to the next function. HOWEVER, if I delete/comment that line and don't display anything in the text field. The codes does continue to the POST function BUT the information from the API call is not passed, i.e my custom class is empty (leading me to believe the callback function is not called at all).
public void getUserData(){
string query = "me?fields=first_name,email,friends";
FB.API (query, HttpMethod.GET, Apicallback, new Dictionary<string, string> ());
}
private void Apicallback(IGraphResult result){
//Parse Graph response into a specific class created for this result.
parsedData = JsonUtility.FromJson<FBData>(result.RawResult);
//Pass each field into UserInfo class.
UserInfo.EMAIL = parsedData.email;
UserInfo.FRIENDS = parsedData.friends;
UserInfo.NAME = parsedData.first_name;
UserInfo.FACEBOOKID = parsedData.id;
/*problem area, if I comment line below, then previous information is apparently not stored. If left as is then testTxt displays correct information but code gets stuck there. */
testTxt.text = "This is the info from USerInfoInside the APICallback: " + UserInfo.EMAIL + UserInfo.FRIENDS + UserInfo.FACEBOOKID;
}
The function below is to send info to php website, is there for illustrative purposes:
code:
public IEnumerator saveUserData() {
//get user info (this information is EMPTY if line in getUserData() is commented.
parsedData.id = UserInfo.FACEBOOKID;
parsedData.friends = UserInfo.FRIENDS;
parsedData.first_name = UserInfo.NAME;
parsedData.email = UserInfo.EMAIL;
//translate data into json
string JsonBodyData = JsonUtility.ToJson (parsedData);
//Custom web request (POST method doesnt seem to work very well, documentation example sends empty form)
var w = new UnityWebRequest(urlSaveUserData, "POST");
byte[] bodyRaw = new System.Text.UTF8Encoding().GetBytes(JsonBodyData);
w.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
w.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
w.SetRequestHeader("Content-Type", "application/json");
yield return w.Send();
//work with received data...}
Im stuck here any help is appreciated. Thanks!
Be sure to use EscapeURL when using strings directly for JSON or HTTP POST and GET methods. The lack of this treatment tends to screw things over, particulary in iOS platforms.
From what I can see, this code
string query = "me?fields=first_name,email,friends";
should instead be escaped as
string query = WWW.EscapeURL("me?fields=first_name,email,friends");
so characters like "?" won't get encoded as an URL symbol.
I'm assuming you don't need to do that for your illustrative example, because UnityWebRequest already escapes your POST request strings internally, but I can't fully confirm that.

Unity Facebook SDK - "FB.API" appears to not save or load score values

I'm creating a game for the Facebook Canvas using the official Facebook SDK. Despite using code directly from the Facebook Unity example, I cannot save nor load a score value. I receive no error or message whatsoever - A value of 0 is always returned when I attempt to retrieve the score. Everything else (logging in, posting to the news feed, etc.) is working fine - just not the score management. Also, this occurs both in the Unity editor and on the canvas itself.
I save the player's score like this:
public void SaveScore(int _score)
{
_fbScore = _score;
var _query = new Dictionary<string, string>();
_query["score"] = _fbScore.ToString();
FB.API("/me/scores", Facebook.HttpMethod.POST, delegate(FBResult r) { Util.Log("Result: " + r.Text); }, _query);
//FB.API ("/me/scores", Facebook.HttpMethod.POST, ScoreSaveCallBack, scoreData);
_hiScoreText.text = _fbScore.ToString();
}
I then try to retrieve the score with this method:
public void GetHighScore()
{
if (_fbIsInited)
FB.API ("/app/scores?fields=score,user.limit(20)", Facebook.HttpMethod.GET, GetScoreCallBack);
}
Callback method:
void GetScoreCallBack(FBResult _result)
{
try
{
List<object> _scoresList = Util.DeserializeScores(_result.Text);
foreach(object _score in _scoresList)
{
var _entry = (Dictionary<string,object>) _score;
var _user = (Dictionary<string,object>) _entry["user"];
string _userId = (string)_user["id"];
if (string.Equals(_userId, FB.UserId))
{
_fbScore = GetScoreFromEntry(_entry);
_hiScoreText.text = _fbScore.ToString();
Debug.Log("FB SCORE: + " + _fbScore);
}
}
}
catch (System.Exception _e)
{
Debug.Log(_e.Message);
}
}
As the score returned is always 0, I do not know whether the problem is with the save or load methods.
EDIT:
To make matters worse, out of nowhere, I've started getting these errors when I attempt to post the score:
You are trying to load data from a www stream which had the following error when downloading.
403 Forbidden
UnityEngine.WWW:get_text()
FBResult:get_Text()
FacebookManager:<SaveScore>m__0(FBResult) (at Assets/Scripts/Facebook/FacebookManager.cs:138)
Facebook.<Start>c__Iterator0:MoveNext()
I haven't made any changes to the code, they just started appearing every time!
Shouldn't you pass a numeric app id in this call instead of 'app'?
FB.API ("/123456789/scores?fields=score,user.limit(20)"
You can find your's app code at FacebookDeveloper dashboard, on your app's main panel - it's the 'application identifier'.
Have you also specified this number in unity Facebook|Facebook Settings|App id? That should fix your problems.

Facebook profile picture doesnt show up, it shows an icon of a question mark

Do you have any idea? I am developing an app using Unity IDE and C#. Also, I'm using the social networking prime31 for my plugin with Facebook. I was able to get all the graphs and display it in my screen app, but since last week it didn't show the profile picture and my friend's picture, it just shows a plain question mark. Do you have any idea with regard to that?
But I was able to show the username and my friend's username. My app token is working, and I am using JSON data to get the data from the Facebook URL.
void Start()
{
getFB_ID();
}
void getFB_ID()
{
Facebook.instance.graphRequest( "me/", HTTPVerb.GET, ( error, obj ) =>
{
var ht = obj as Hashtable;
userId = ht["id"].ToString();
Debug.Log( "USER ID: " + userId);
string url = "http://graph.facebook.com/"+userId+"?fields=id,name,picture";
StartCoroutine(getURL(url));
});
}
IEnumerator getURL(string url)
{
WWW www = new WWW(url);
yield return www;
Debug.Log ("Heres the URL you are accessing: " + url);
ProfilePicDisplay(www.text);
public void ProfilePicDisplay(string jsonString)
{
JsonData jsonProfilePic = JsonMapper.ToObject(jsonString);
ConverterScript fbprofilepic;
MyPicture = new ArrayList();
{
fbprofilepic = new ConverterScript();
fbprofilepic.name = jsonProfilePic["name"].ToString();
fbprofilepic.picture = jsonProfilePic["picture"].ToString();
LoadProfilePic(fbprofilepic);
MyPicture.Add(fbprofilepic.name);
}
}
private void LoadProfilePic(ConverterScript profile)
{
string ProfilePic = "userAvatar";
GameObject profile_pic_holder = GameObject.Find(ProfilePic);
profile_pic_holder.SendMessage("LoadImage", profile);
}
was able to get the data in my logs, but the problem is it didnt load the image, it says:
You are trying to load data from a www stream which had the following error when downloading.
Could not resolve host: JsonData object (Domain name not found)
As of last week, October 3rd and detailed here: https://developers.facebook.com/blog/post/2012/10/03/platform-updates--operation-developer-love/
The picture endpoint no longer returns the picture URL in the string. It returns a dictionary and you have to change your parsing to get to it. So the data returned will look more like this:
{
"id": "1234567",
"name": "Your Name",
"picture": {
"data": {
"url": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-prn1/your_picture.jpg",
"is_silhouette": false
}
}
}
In the latest LeanLoader, there is an automatic fix for the newly implimented Facebook endpoint abernathy mentioned, which allows you to load the image directly (with all the parsing of the json and other data handled by the engine). You can easily load profile images like this:
function Start () {
LeanLoader.load("https://graph.facebook.com/DentedPixel/picture?type=large&redirect=false", LLOptions().setOnLoad(onImageLoaded));
}
private function onImageLoaded( tex:Texture2D ){
Debug.Log("Your image texture ready to use! :"+tex);
}
There is also many other features that I think people will find helpful with LeanLoader (including caching of images/text/sound and a built-in JSON parser).
try this- fb sdk version-9.1.0
FB.API ("/me/picture?type=large", HttpMethod.GET, DisplayProfilePic);// API call
void DisplayProfilePic(IGraphResult result)
{
Image profilePic;
if (result.Texture != null)
{
profilePic = image1; // diplaying image
profilePic.sprite = Sprite.Create(result.Texture, new Rect(0, 0, result.Texture.width, result.Texture.height), new Vector2());
}
}