Firstly I must point out that I am very new to C#.
I am developing an application using Unity3D, and part of the application requires that I parse a JSON file stored on my server.
The problem that I am having is that sometimes everything works perfectly, and other times the app hangs on the downloading the JSON. I don't receive any errors, the script just never reaches 100% on the progress.
Here is my code:
public IEnumerator DownloadJSONFile(string url)
{
Debug.Log("JSON URL: "+ url);
mJsonInfo = new WWW(url);
yield return mJsonInfo;
mIsJSONRequested = true;
}
private void LoadJSONData(string jsonUrl)
{
Debug.LogWarning("LoadJSONData, url= "+jsonUrl);
if(!mIsJSONRequested){
StartCoroutine(DownloadJSONFile(jsonUrl));
} else {
if(mJsonInfo.progress >= 1)
{
if(mJsonInfo.error == null )
{
//** PARSE THE JSON HERE **//
}else
{
Debug.LogError("Error downloading JSON");
mIsLoadingData = false;
}
} else {
Debug.LogWarning("!! ### JSON DOWNLOADING: "+mJsonInfo.progress+"%");
if(mJsonInfo.error != null )
{
Debug.LogError("Error downloading JSON");
Debug.LogError("JSON Error:"+mJsonInfo.error);
mIsLoadingData = false;
}
}
}
}
Like I said, 50% of the time the JSON data gets loaded nearly instantly, and 50% of the time the progress never reaches 1. I never receive an error in form the mJsonInfo.error variable.
Any suggestions as to what I am doing wrong would be greatly appreciated!
You need to wait until the download is complete.
As stated in the documentation you need to:
var www = new WWW(...);
yield return www;
So you need to modify the return type of your method from void to IEnumerator.
private IEnumerator LoadJSONData(string jsonUrl)
{
Debug.LogWarning("LoadJSONData, url= "+jsonUrl);
if(!mIsJSONRequested)
{
// Gets the json book info from the url
mJsonInfo = new WWW(jsonUrl);
yield return mJsonInfo; //Wait for download to complete
mIsJSONRequested = true;
}
else
{
...
}
}
isDone is that you need,
WWW lWWW = new WWW(...)
if(lWWW.isDone)
then parse it
I found the problem, I thought I would post my solution for anyone else experiencing the same problem.
The solution in the end was the JSON file on the server. I was using PHP (CakePHP) to generate the JSON, when opening the PHP generated file via the browser the response time was instant, but from my mobile app for some reason it would hang. So I changed my server side code to actually create and updated an actual JSON file, and now everything works fine.
Related
After some advise around calls to firebase storage primarily how (from a sync POV) I should be checking to see if a file exists.
To set the context I am reading in a state file as json using the DownloadCoroutine function below and passing this into a JsonTextReader, this is working fine as long as the file exists in the first place (which as a new user it will not).
So then I wrote the below checkIfFile exists function which also works in a standalone capacity (Grabs the URL to prove that this file does in fact exist). Once this function has completed I then set a bool (SaveFileExists) to say this is/is not an existing file then go create one dependent on the state.
Where my problem lies is the order in which these functions are executed, I need the check to happen before any other methods are executed, they are both called in the LoadScene function currently. What I think I need to do is make the check an Async method returning a task? If so how would this look and where should I be calling it from, I have tried this but I think it keeps locking the main thread.
So the state right now is that because that bool doesnt change in time, the download of the storage file doesnt happen and the Json is never read in and throws an error, at the end of the console output the checkfile URL is outputted, any help would be great ,thanks.
private IEnumerator DownloadCoroutine(string path)
{
var storage = FirebaseStorage.DefaultInstance;
var storageReference = storage.GetReference(path);
if (SaveFileExists == true)
{
var DownloadTask = storageReference.GetBytesAsync(long.MaxValue);
yield return new WaitUntil(predicate: () => DownloadTask.IsCompleted);
byte[] fileContents = DownloadTask.Result;
retrievedSaveFile = Encoding.Default.GetString(fileContents);
Debug.Log("Downloading the save file");
}
else
{
createNewSaveFile(path);
}
}
Check to see if the Json file exists
private void CheckIfFileExists(string path)
{
var storage = FirebaseStorage.DefaultInstance;
var storageReference = storage.GetReference(path);
storageReference.GetDownloadUrlAsync().ContinueWith(task => {
if (!task.IsFaulted && !task.IsCanceled) {
Debug.Log("Download URL: " + task.Result);
SaveFileExists = true;
}
else{
Debug.Log("file doesnt exist so we create one");
}
});
}
Load scene
public IEnumerator LoadLastScene()
{
var User = FirebaseAuth.DefaultInstance.CurrentUser;
Debug.Log("USERID IS " + User.UserId.ToString());
CheckIfFileExists("Saves://" + User.UserId.ToString() + "saveFile.json");
yield return DownloadCoroutine("Saves://" + User.UserId.ToString() +
"saveFile.json");
}
The things you have to do is that
User authentication
Everything must be inside an async task function
check if the file exists
await storageRef.Root.Child(fUser.UserId).Child("Data").GetDownloadUrlAsync().ContinueWith(async task2 =>
{
if (task2.IsFaulted || task2.IsCanceled)
{
Debug.Log("<color=Red>File Not Exists</color>");
}
else
{
Debug.Log("<color=green>File Exists</color>");
await DownloadData();
}
});
Good Day Community,
Im Working on a silly drinking game since a few weeks. Its all fine and dandy. Worked my way through all my ideas and got it done. I was pretty proud of my self. Now hereĀ“s the kicker. The game downloads a Json file from my Nextcloud using www in Unity. The Json Holds all the Game_Cards. It worked Really well in Unity, But after i build the game, Its just doing nothing. Im pretty Sure im missing something here. But i have no Clue how to Debug it. And to be frank, i think i have Workblindness on this one. I hope its a simple fix.
What i think is going on Maybe:
Download works Fine but Json file is saved to wrong or inaccesible Path
Download is not Working at all.
Card Path:
cardPath = Application.dataPath + "/Saves/cards.json";
Download Method
IEnumerator Cards()
{
UnityWebRequest www = new UnityWebRequest("https://my.link.wich/nobody/should/know");
www.downloadHandler = new DownloadHandlerBuffer();
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.LogError(www.error);
}
else
{
string downloadedText = www.downloadHandler.text;
File.WriteAllText(cardPath, downloadedText);
ReadCards();
}
}
Reading Cards from Json
public void ReadCards()
{
string cardFile = File.ReadAllText(cardPath);
cardDatabase = JsonUtility.FromJson<CardListObject>(cardFile);
}
First time using StackOverflow an actually posting something. Be kind on me :D
Update
Got it Working!
I had to create the Folder first.
cardPath = Application.persistentDataPath + "/Saves/cards.json";
if (!System.IO.Directory.Exists(Application.persistentDataPath + "/Saves"))
{
System.IO.Directory.CreateDirectory(Application.persistentDataPath + "/Saves");
}
In build, you need to use Application.persistentDataPath not Application.dataPath
UPD: My bad, you are reading only if downloading. So you can ignore next part.
You need to do something in your ReadCards method in case the file does not exists, because you can have a download error and file will not be created.
For example:
public void ReadCards()
{
if (!System.IO.File.Exists(cardPath) return;
string cardFile = File.ReadAllText(cardPath);
cardDatabase = JsonUtility.FromJson<CardListObject>(cardFile);
}
Then your cardDatabase will stay with default values and you will not have a FileNotFoundException.
I have the following upload code using Unity's UnityWebRequest API (Unity 2019.2.13f1):
public IEnumerator UploadJobFile(string jobId, string path)
{
if (!File.Exists(path))
{
Debug.LogError("The given file to upload does not exist. Please re-create the recording and try again.");
yield break;
}
UnityWebRequest upload = new UnityWebRequest(hostURL + "/jobs/upload/" + jobId);
upload.uploadHandler = new UploadHandlerFile(path);
upload.downloadHandler = new DownloadHandlerBuffer();
upload.method = UnityWebRequest.kHttpVerbPOST;
upload.SetRequestHeader("filename", Path.GetFileName(path));
UnityWebRequestAsyncOperation op = upload.SendWebRequest();
while (!upload.isDone)
{
//Debug.Log("Uploading file...");
Debug.Log("Uploading file. Progress " + (int)(upload.uploadProgress * 100f) + "%"); // <-----------------
yield return null;
}
if (upload.isNetworkError || upload.isHttpError)
{
Debug.LogError("Upload error:\n" + upload.error);
}
else
{
Debug.Log("Upload success");
}
// this is needed to clear resources on the file
upload.Dispose();
}
string hostURL = "http://localhost:8080";
string jobId = "manualUploadTest";
string path = "E:/Videos/short.mp4";
void Update()
{
if (Input.GetKeyDown(KeyCode.O))
{
Debug.Log("O key was pressed.");
StartCoroutine(UploadAndTest(jobId, path));
}
}
And the files I receive on the server side arrive broken, especially if they are larger (30 MB or more). They are missing bytes in the end and sometimes have entire byte blocks duplicated in the middle.
This happens both when testing client and server on the same machine or when running on different machines.
The server does not complain - from its perspective, no transport errors happened.
I noticed that if I comment out the access to upload.uploadProgress (and e.g. instead use the commented-out debug line above it which just prints a string literal), the files stay intact. Ditching the wile loop altogether and replacing it with yield return op also works.
I tested this strange behavior repeatedly in an outer loop - usually after at most 8 repetitions with the "faulty" code, the file appears broken. If I use the "correct" variant, 100 uploads (update: 500) in a row were successful.
Has upload.uploadProgress side-effects? For what it's worth, the same happens if I print op.progress instead - the files are also broken.
This sounds like a real bug. uploadProgress obviously should not have side effects.
So I have been trying to make it possible for users to load a .obj file and read it as an AssetBundle, but I can't figure it out.
I have figured out how to get the path of the file, but I can't load it as an asset bundle, it just returns null.
Here is my code :
WWW bundleRequest = new WWW(#"file://" + pathName);
while (!bundleRequest.isDone)
{
yield return null;
}
AssetBundle bundle = null;
if (bundleRequest.bytesDownloaded > 0)
{
AssetBundleCreateRequest myRequest = AssetBundle.LoadFromMemoryAsync(bundleRequest.bytes);
while (!myRequest.isDone)
{
Debug.Log("loading....");
yield return null;
}
if (myRequest.assetBundle != null)
{
bundle = myRequest.assetBundle;
GameObject model = null;
if (bundle != null)
{
AssetBundleRequest newRequest = bundle.LoadAssetAsync<GameObject>("Test");
while (!newRequest.isDone)
{
Debug.Log("loading ASSET....");
yield return null;
}
model = (GameObject)newRequest.asset;
bundle.Unload(false);
}
}
else
{
Debug.LogError("COULDN'T DOWNLOAD ASSET BUNDLE FROM URL");
}
}
else
{
Debug.LogError("COULDN'T DOWNLOAD ASSET BUNDLE FROM URL");
}
pathName here is: "C:\\Users\\mySuperCoolName\\OneDrive\\Documents\\Fun\\Programming\\Ungoing projects\\ThiefCop\\Unity Mobile\\Assets\\Prefabs\\TestOBJ.obj". Everything seems to work until AssetBundleCreateRequest when AssetBundle.LoadFromMemoryAsync() is called, where myRequest.assetBundle == null even if the file was downloaded correctly.
I also get an error which probably is linked with my problem : I have searched for what it meant but I couldn't find...
It is really hard to explain what I mean, but I really hope you can find an answer to this, I've been searching for hours and between us, I don't understand much of File loadind and Reading... Don't hesitate to ask if you didn't understand my bad english... Thank you in advance :)
https://docs.unity3d.com/ScriptReference/BuildPipeline.BuildAssetBundles.html
To be short :
Import your object in Unity
Give it a AssetBundle name (click on it then on bottom of the inspector view)
Call this function
for load obj files directly to you project, you need todo code what unpacks object file and then converts it to the Unity engine.
in assert store look at the Runtime OBJ Importer,
this should be right direction for your issue.
https://assetstore.unity.com/packages/tools/modeling/runtime-obj-importer-49547
Building a Unity front-end app that communicates with a PHP/MySQL back-end. Right now I am working on the register and login portions of it. In the Unity editor, all works perfectly, on the browser front end registration is working fine as well. But as soon as I take the working build in Unity and build it to test on my Google Pixel phone, registration and login both fail on my phone. The error message I am getting on Login is "Error parsing response from server, please try again!" which is in my UILogin.cs. Yet there is nothing else attached. I tried following the Unity documentation for debugging, adb, and DDMS to get access to find out what is happening but I have failed on all those ventures. Is there something particularly different about Android and WWW objects or web communications? Here is a portion of my UILogin.cs and also will include the Login.php.
UILogin.cs
IEnumerator AttemptLogin(bool quickLogin)
{
CreateLoadingScreen();
DeactivateForm();
WWWForm form = new WWWForm();
form.AddField("email", Email.text);
form.AddField("password", Password.text);
WWW www = new WWW(URL.GetLoginURL, form);
yield return www;
DestroyLoadingScreen();
ActivateForm();
ParseResult(www.text, quickLogin);
}
void ParseResult(string result, bool quickLogin)
{
byte resultCode;
if (!byte.TryParse(result, out resultCode))
Result.text = "Error parsing response from server, please try again!";
else if (resultCode == 0)
{
if (RememberToggle.isOn && !quickLogin) // Remember me
{
PlayerPrefs.SetInt("remember", 1);
PlayerPrefs.SetString("email", Email.text);
PlayerPrefs.SetString("password", Password.text);
}
else if (!quickLogin)
{
TemporaryAccount.Email = Email.text;
TemporaryAccount.Password = Password.text;
}
SceneManager.LoadScene(3);
}
else // Failure
{
if (quickLogin)
ShowForm();
Result.text = WebError.GetLoginError(resultCode);
}
}
Login.php
<?php
require "conn.php";
$stmt = $pdo->prepare("SELECT * FROM account WHERE email=:email");
$stmt->bindParam(":email", $_POST['email']);
$stmt->execute();
$count = $stmt->rowCount(); // gets count of records found
if($count > 0) {
$result = $stmt->fetch(); // gets resultset
if(!password_verify($_POST['password'], $result[4]))
echo "2";
else
echo "0";
}
else {
echo "1";
}
?>
Any ideas?
Update #1
I printed www.text and www.error after the yield return www; Text is null and error says: Unknown Error.
Update #2
Interestingly enough, I get Unknown Error from this as well:
Clickme.cs
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class Clickme : MonoBehaviour
{
public Text Result;
public void OnClick()
{
StartCoroutine(RunScript());
}
IEnumerator RunScript()
{
WWW www = new WWW("http://www.familypolaris.com/helloWorld.php");
yield return www;
if (www.text == "")
Result.text = "Result was empty, error: " + www.error;
}
}
HelloWorld.php
<?php
echo "Hello World!";
?>
I figured it out, so I will post the answer here. After somebody commented that they tried my code and it worked just fine, I decided to try another device here. That device also failed to register and login. This told me something was wrong with my build settings. My first idea was to switch build type from Gradle to Internal, and that solved the problem for both my devices immediately.