I am creating a HoloLens app using Unity which has to take data from a REST API and display it.
I am currently using WWW datatype to get the data and yield return statement in a coroutine that will be called from the Update() function. When I try to run the code, I get the latest data from the API but when someone pushes any new data onto the API, it does not automatically get the latest data in real time and I have to restart the app to see the latest data.
My Code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
public class TextChange : MonoBehaviour {
// Use this for initialization
WWW get;
public static string getreq;
Text text;
bool continueRequest = false;
void Start()
{
StartCoroutine(WaitForRequest());
text = GetComponent<Text>();
}
// Update is called once per frame
void Update()
{
}
private IEnumerator WaitForRequest()
{
if (continueRequest)
yield break;
continueRequest = true;
float requestFrequencyInSec = 5f; //Update after every 5 seconds
WaitForSeconds waitTime = new WaitForSeconds(requestFrequencyInSec);
while (continueRequest)
{
string url = "API Link goes Here";
WWW get = new WWW(url);
yield return get;
getreq = get.text;
//check for errors
if (get.error == null)
{
string json = #getreq;
List<MyJSC> data = JsonConvert.DeserializeObject<List<MyJSC>>(json);
int l = data.Count;
text.text = "Data: " + data[l - 1].content;
}
else
{
Debug.Log("Error!-> " + get.error);
}
yield return waitTime; //Wait for requestFrequencyInSec time
}
}
void stopRequest()
{
continueRequest = false;
}
}
public class MyJSC
{
public string _id;
public string author;
public string content;
public string _v;
public string date;
}
This is happening because resources caching is enabled on the Server.
Three possible solutions I know about:
1.Disable resources caching on the server. Instructions are different for every web server. Usually done in .htaccess.
2.Make each request with unique timestamp. The time should in Unix format.
This method will not work on iOS. You are fine since this is for HoloLens.
For example, if your url is http://url.com/file.rar, append ?t=currentTime at the end. currentTime is the actual time in Unix Format.
Full example url: http://url.com/file.rar?t=1468475141
Code:
string getUTCTime()
{
System.Int32 unixTimestamp = (System.Int32)(System.DateTime.UtcNow.Subtract(new System.DateTime(1970, 1, 1))).TotalSeconds;
return unixTimestamp.ToString();
}
private IEnumerator WaitForRequest()
{
string url = "API Link goes Here" + "?t=" + getUTCTime();
WWW get = new WWW(url);
yield return get;
getreq = get.text;
//check for errors
if (get.error == null)
{
string json = #getreq;
List<MyJSC> data = JsonConvert.DeserializeObject<List<MyJSC>>(json);
int l = data.Count;
text.text = "Data: " + data[l - 1].content;
}
else
{
Debug.Log("Error!-> " + get.error);
}
}
3.Disable Cache on the client side by supplying and modifying the Cache-Control and Pragma headers in the request.
Set Cache-Control header to max-age=0, no-cache, no-store then set Pragma header to no-cache.
I suggest you do this with UnityWebRequest instead of the WWW class. First, Include using UnityEngine.Networking;.
Code:
IEnumerator WaitForRequest(string url)
{
UnityWebRequest www = UnityWebRequest.Get(url);
www.SetRequestHeader("Cache-Control", "max-age=0, no-cache, no-store");
www.SetRequestHeader("Pragma", "no-cache");
yield return www.Send();
if (www.isError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Received " + www.downloadHandler.text);
}
}
Related
I'm using an open-source game to write a script, that captures the screen frames in a collection and sends the collection to an API server.
Capturing works fine, but as soon as the API request fires, the game freezes for ~500ms. If I understand correctly, this request blocks the main thread.
I've tried to send the request in a different thread, but get some weird errors.
How can I send requests in a background, so it doesn't block the main thread?
Here's my code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using System.Threading.Tasks;
public class Recorder : MonoBehaviour
{
private float captureInterval = 1f / 24f;
private readonly List<string> frames = new();
async void Update()
{
if (Time.timeSinceLevelLoad >= captureInterval)
{
StartCoroutine(CaptureFrame());
captureInterval = Time.timeSinceLevelLoad + (1f / 24f);
if (frames.Count >= 120)
{
StartCoroutine(CommitFrames(frames));
frames.Clear();
}
}
}
IEnumerator CaptureFrame()
{
yield return new WaitForEndOfFrame();
Texture2D texture = ScreenCapture.CaptureScreenshotAsTexture();
byte[] jpg = texture.EncodeToJPG();
string base64 = Convert.ToBase64String(jpg);
frames.Add(base64);
Destroy(texture);
}
IEnumerator CommitFrames(List<string> framesData)
{
var payload = new CommitFramesData() { frames = framesData };
var www = CreateRequest("http://my-api.test/api/frames", RequestType.POST, payload);
yield return www.SendWebRequest();
www.Dispose();
}
private UnityWebRequest CreateRequest(string path, RequestType type = RequestType.GET, object data = null)
{
var request = new UnityWebRequest(path, type.ToString());
if (data != null)
{
var bodyRaw = Encoding.UTF8.GetBytes(JsonUtility.ToJson(data));
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
}
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
return request;
}
}
public enum RequestType
{
GET = 0,
POST = 1,
PUT = 2
}
[Serializable]
class CommitFramesData
{
public List<string> frames;
}
for my unity game i am trying to make an end score screen where at the end of a level the players imputed name and score are put into a database. i have linked all the game objects correctly but the script doesn't even give an error code and i cant see what i have missed. i have now tried without the adding the score (only adding a imputed name) but i still get the same issue
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class AddScore : MonoBehaviour
{
public InputField nameField;
public GameObject Time;
public GameObject ScoreText;
public Button submitScore;
public void InputScore()
{
StartCoroutine(Register());
}
IEnumerator Register()
{
WWWForm form = new WWWForm();
form.AddField("name", nameField.text);
UnityWebRequest www = UnityWebRequest.Get("https://localhost/sqlconnect/addscore.php");
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.LogError(www.error);
}
else
{
Debug.Log("Score added");
UnityEngine.SceneManagement.SceneManager.LoadScene(0);
}
}
public void VerifyInputs()
{
submitScore.interactable = (nameField.text.Length <= 8);
}
}
i can't tell if it is an issue with the php or the c# code so i put them both up here
<?php
$con = mysqli_connect('localhost', 'root', 'password', 'Leaderboard');
if (mysqli_connect_error())
(
echo"1; Connection failed";
exit()
)
$username = $_POST["name"];
$insertuserquery = "INSERT INTO addscore(username) VALUES ('" . $username . "');";
mysqli_query($con, $insertuserquery) or die("4: Insert addscore query failed");
echo("0")
?>
You are doing a GET request without any body:
UnityWebRequest www = UnityWebRequest.Get("https://localhost/sqlconnect/addscore.php");
You should be doing a POST request with the username parameter in the body
Unity POST data
Example Code:
WWWForm form = new WWWForm();
form.AddField("name", "myUserName");
using (UnityWebRequest www = UnityWebRequest.Post("https://localhost/sqlconnect/addscore.php", form))
{
yield return www.SendWebRequest();
{
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("Form upload complete!");
}
}
This question already has an answer here:
You are using download over http. Currently Unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition
(1 answer)
Closed 4 years ago.
I am uploading data to database using WWW.
I have two data classes for data object.
[Serializable]
public class pushdatawrapper{
public string email;
public pushdata[] shotsArray=new pushdata[1];
}
[Serializable]
public class pushdata{
public string timehappened;
public string clubtype;
public pushdata(string timestamp_,string clubtype_)
{
timehappened = timestamp_;
clubtype = clubtype_;
}
}
Loading Json array of data object to database, I tried two approaches.
The first one was ok, but the second one is always failed at calling StartCoroutine function.
First approach
public class BLEBridge : MonoBehaviour {
void Start () {
pushdata_ pdata = new pushdata_("123123123123.0","15","123.0","123.0", "123.0", "123.0","123.0", "123.0","123.0","123.0","123.0","123.0", "First");
pushdatawrapper_ pw = new pushdatawrapper_ ();
pw.email = "test#gmail.com";
pw.shotsArray[0] = pdata;
StartCoroutine (PushData_ (pw));
}
private IEnumerator PushData_(pushdatawrapper_ pdata){
WWW www;
Hashtable postHeader = new Hashtable();
postHeader.Add("Content-Type", "application/json");
string dataToJason = JsonUtility.ToJson(pdata);
Debug.Log ("dataToJason " + dataToJason);
// convert json string to byte
var formData = System.Text.Encoding.UTF8.GetBytes(dataToJason);
www = new WWW("http://rmotion.rapsodo.com/api/push/new", formData, postHeader);
StartCoroutine(WaitForRequest(www));
return www;
}
IEnumerator WaitForRequest(WWW data)
{
yield return data; // Wait until the download is done
if (data.error != null)
{
Debug.Log("There was an error sending request: " + data.text);
}
else
{
Debug.Log("WWW Request: " + data.text);
}
}
}
This first method is always ok.
But in actual implementation, I need to upload data from a static function. Those private IEnumerator PushData_(pushdatawrapper_ pdata) and IEnumerator WaitForRequest(WWW data) can't be static.
So what I did was
I made a separate class as
public class UploadData: MonoBehaviour{
public void uploadindividualshot(pushdatawrapper pw){
StartCoroutine (PushData (pw));
}
private IEnumerator PushData(pushdatawrapper pdata){
WWW www;
Hashtable postHeader = new Hashtable();
postHeader.Add("Content-Type", "application/json");
string dataToJason = JsonUtility.ToJson(pdata);
Debug.Log ("dataToJason " + dataToJason);
// convert json string to byte
var formData = System.Text.Encoding.UTF8.GetBytes(dataToJason);
Debug.Log ("before www ");
www = new WWW("http://rmotion.rapsodo.com/api/push/new", formData, postHeader);
Debug.Log ("after new ");
StartCoroutine(WaitForRequest(www));
Debug.Log ("after WaitForRequest ");
return www;
}
IEnumerator WaitForRequest(WWW data)
{
Debug.Log ("start pushing ");
yield return data; // Wait until the download is done
if (data.error != null)
{
Debug.Log("There was an error sending request: " + data.text);
}
else
{
Debug.Log("WWW Request: " + data.text);
}
}
}
Then call the instance of the class from static method as
public class BLEBridge : MonoBehaviour {
void Start(){}
public unsafe static void GetGolfREsult()
{
pushdata pdata = new pushdata("123123123123.0","15","123.0","123.0", "123.0", "123.0","123.0", "123.0","123.0","123.0","123.0","123.0", "First");
pushdatawrapper pw = new pushdatawrapper();
pw.email = Login.loginName;
pw.shotsArray[0] = pdata;
UploadData up = new UploadData ();
up.uploadindividualshot (pw);
}
}
I always have error as
after new
UploadData:PushData(pushdatawrapper)
BLEBridge:GetGolfREsult()
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
libc++abi.dylib: terminating with uncaught exception of type Il2CppExceptionWrapper
2018-04-29 17:25:25.565292+0800 RMotion[270:7241] You are using download over http. Currently Unity adds NSAllowsArbitraryLoads to Info.plist to simplify transition, but it will be removed soon. Please consider updating to https.
The program has error at StartCoroutine(WaitForRequest(www));
What is wrong with second approach?
In unity calling this line of code automatically start http request
WWW www = new WWW("url");
yield is just the convenience way to tell the coroutine to wait for the process to finish. Don't separate your calls in 2 IEnumerator.
private IEnumerator PushData_(pushdatawrapper_ pdata){
WWW www;
Hashtable postHeader = new Hashtable();
postHeader.Add("Content-Type", "application/json");
string dataToJason = JsonUtility.ToJson(pdata);
Debug.Log ("dataToJason " + dataToJason);
// convert json string to byte
var formData = System.Text.Encoding.UTF8.GetBytes(dataToJason);
www = new WWW("http://rmotion.rapsodo.com/api/push/new", formData, postHeader)
Debug.Log ("start pushing ");
yield return www; // Wait until the download is done
if (www.error != null)
{
Debug.Log("There was an error sending request: " + www.text);
}
else
{
Debug.Log("WWW Request: " + www.text);
}
}
And I don't see any use of pointer in your static method so why did you use the unsafe keyword?
I have this script to interact with my database which is on mySql
string dataSubmitURL = "http://localhost/HighScore/AddScore.php?";
string dataGetURL = "http://localhost/HighScore/GetScore.php?";
// Use this for initialization
void Start () {
StartCoroutine(GetScores());
}
// Update is called once per frame
void Update () {
}
IEnumerator PostScores(string playerName, int score) {
string submitURL = dataSubmitURL + "name="+ playerName + "&score=" + score;
print(submitURL);
WWW submitData = new WWW(submitURL);
yield return submitData;
if (submitData.error != null)
{
Debug.LogError("Error occur when Submitting data : " + submitData.error);
Debug.LogError("Error occur when Submitting data : " + submitData.text);
Debug.LogError("Error occur when Submitting data : " + submitData.responseHeaders);
//submitData.text
}
else {
print(" Submitted");
}
IEnumerator GetScores() {
WWW getData = new WWW(dataGetURL);
yield return getData;
if (getData.error != null)
{
Debug.LogError("There was an error getting the high score: " + getData.error);
}
else {
print(getData.text);
}
}
}
But the problem is i am getting
There was an error getting the high score: Empty reply from server
Although these both urls
string dataSubmitURL = "http://localhost/HighScore/AddScore.php?";
string dataGetURL = "http://localhost/HighScore/GetScore.php?";
working fine in browser when i directly put it. I also added this crossDomain.xml in my root folder so that it can accessible to all. What I am doing wrong.
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>
I don't know what was the problem all the code is correct and it works by changing the IP Address only.
string dataSubmitURL = "http://YouripAddress/HighScore/AddScore.php?";
string dataGetURL = "http://YouripAddress/HighScore/GetScore.php?";
The Code below is used to get Temperature Value from Thingworx server that is hosted in one of our own systems. This works perfectly well in unity. But not in andoird, once apk is generated, it won't fetch any data from the server and there will be connection established. But, it just wont fetch the data and put that into the text mesh.
I'm using unity 5.4.1 32bit . Check in both Android - 5.0.2 and 6.
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using System.Text.RegularExpressions;
using System;
using UnityEngine.UI;
public class GETTempValue : MonoBehaviour {
public GameObject TempText;
static string TempValue;
void Start()
{
StartCoroutine(GetText());
}
IEnumerator GetText()
{
Debug.Log("Inside Coroutine");
while (true)
{
yield return new WaitForSeconds(5f);
string url = "http://Administrator:ZZh7y6dn#*IP Address*:8080/Thingworx/Things/SimulationData/Properties/OvenTemperature/";
Debug.Log("Before UnityWebRequest");
UnityWebRequest www = UnityWebRequest.Get (url);
yield return www.Send();
Debug.Log("After UnityWebRequest");
if (www.isError) {
Debug.Log ("Error while Receiving: "+www.error);
} else {
Debug.Log("Success. Received: "+www.downloadHandler.text);
string result = www.downloadHandler.text;
Char delimiter = '>';
String[] substrings = result.Split(delimiter);
foreach (var substring in substrings)
{
if (substring.Contains ("</TD"))
{
String[] Substrings1 = substring.Split ('<');
Debug.Log (Substrings1[0].ToString()+"Temp Value");
TempValue = Substrings1 [0].ToString ();
TempText.GetComponent<TextMesh> ().text = TempValue+"'C";
}
}
}
}
}
}
this is the android manifest permission
uses-permission android:name="android.permission.INTERNET"
uses-permission android:name="android.permission.CAMERA"
uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"
uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
Embedding username and password(http://username:password#example.com) in a url is no longer supported in some Applications and OS for security reasons.That's because this is not the standard way to perform HTTP Authentication. It very likely that Unity or Android did not implement this on the their side.
I tested this on the built-in Android Browser with http://Administrator:ZZh7y6dn#*IP Address*:8080/Thingworx/Things/SimulationData/Properties/OvenTemperature/ and it failed to function. So, I guess this problem is from Android.
I tested again without username and password http://*IP Address**:8080/Thingworx/Things/SimulationData/Properties/OvenTemperature/ then the login window appeared. When I entered the username and password, it worked.
You can still use UnityWebRequest to solve this problem by providing the AUTHORIZATION header to the UnityWebRequest with the SetRequestHeader function. This will only work if the authorization type is Basic instead of Digest. In your case, it is HTTP Basic.
For general solution:
string authenticate(string username, string password)
{
string auth = username + ":" + password;
auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
auth = "Basic " + auth;
return auth;
}
IEnumerator makeRequest()
{
string authorization = authenticate("YourUserName", "YourPassWord");
string url = "yourUrlWithoutUsernameAndPassword";
UnityWebRequest www = UnityWebRequest.Get(url);
www.SetRequestHeader("AUTHORIZATION", authorization);
yield return www.Send();
.......
}
For solution in your question:
public GameObject TempText;
static string TempValue;
void Start()
{
StartCoroutine(GetText());
}
string authenticate(string username, string password)
{
string auth = username + ":" + password;
auth = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth));
auth = "Basic " + auth;
return auth;
}
IEnumerator GetText()
{
WaitForSeconds waitTime = new WaitForSeconds(2f); //Do the memory allocation once
string authorization = authenticate("Administrator", "ZZh7y6dn");
while (true)
{
yield return waitTime;
string url = "http://*IP Address*:8080/Thingworx/Things/SimulationData/Properties/OvenTemperature/";
UnityWebRequest www = UnityWebRequest.Get(url);
www.SetRequestHeader("AUTHORIZATION", authorization);
yield return www.Send();
if (www.isError)
{
Debug.Log("Error while Receiving: " + www.error);
}
else
{
string result = www.downloadHandler.text;
Char delimiter = '>';
String[] substrings = result.Split(delimiter);
foreach (var substring in substrings)
{
if (substring.Contains("</TD"))
{
String[] Substrings1 = substring.Split('<');
Debug.Log(Substrings1[0].ToString() + "Temp Value");
TempValue = Substrings1[0].ToString();
TempText.GetComponent<TextMesh>().text = TempValue + "'C";
}
}
}
}
}