[SOLVED] UnityEngine.WWW does not work in webplayer?

Hi all.

It seems, that the Unity class “WWW” does not work in webplayer.

In Unity Editor on Windows and Mac, in Windows and Mac Standalone, even on iPhone device all works fine and I get an instant answer from the server, in Webplayer (not matter, which browser or OS) there is no answer at all and Wireshark tells me, there is even never sent any request to the server.

I wrote the following sample, to demonstrate the issue:

using UnityEngine;
using System;
using System.Collections;

public class testWWW: MonoBehaviour
{
	// VARIABLES
	private bool readyToQuit = true;
	string output = "";
	
	private enum State : byte
	{
		None,
		LogIn,
		LoggingIn,
	}
	private byte state = (byte)State.None;

	private enum GUIState : byte
	{
		None,
		LoggedOut,
	}
	private byte guiState = (byte)GUIState.None;

	// METHODS

	// Use this for initialization
	void Start ()
	{
	}

	// Update is called once per frame
	void Update ()
	{
		applicationMain();
	}
	
	void OnGUI()
	{
		switch(guiState)
		{
		case (byte)GUIState.None:
			// show no GUI at all
			break;
		case (byte)GUIState.LoggedOut:
			if (GUI.Button(new Rect(100, 100, 100, 100), "Login"))
				state = (byte)State.LogIn;
			break;
		default:
			break;
		}
		GUI.Label(new Rect(0, 0, 250, 20), output);
	}

	void OnApplicationQuit()
	{
		while(!readyToQuit)
			applicationMain();
	}

	string Request (string url, float timeout)
	{ 
		Debug.Log("requesting from "+url); 
		WWW www = new WWW(url); 
		float stop = Time.realtimeSinceStartup+timeout;
		float current = Time.realtimeSinceStartup;
		while ((![url]www.isDone[/url])  (stop > Time.realtimeSinceStartup))
		{
			if(current != Time.realtimeSinceStartup)
			{
				current = Time.realtimeSinceStartup;
				Debug.Log(stop + "   " + Time.realtimeSinceStartup);
			}
			Sleep(1);
		} 
		if ([url]www.isDone[/url])
		{
			output = "response: "+[url]www.data;[/url]
			Debug.Log(output);
			return [url]www.data;[/url] 
		}
		else
		{
			output = "timeout!";
			Debug.Log(output);
			return ""; 
		}
	}
	
	IEnumerator Sleep(float secs)
	{
		yield return StartCoroutine(SleepSecs(1)); 
	}

	IEnumerator SleepSecs (float secs)
	{ 
		yield return new WaitForSeconds(secs); 
	} 

	private void Test()
	{
		Request("http://google.com", 5);
	}

	private void applicationMain()
	{
		switch(state)
		{
		case (byte)State.None:
			guiState = (byte)GUIState.LoggedOut;
			readyToQuit = true;
			break;
		case (byte)State.LogIn:
			state = (byte)State.LoggingIn;
			guiState = (byte)GUIState.None;
			readyToQuit = false;
			output = "waiting for answer from server...";
			break;
		case (byte)State.LoggingIn:
			Test();
			state = (byte)State.None;
			break;
		default:
			break;
		}
	}
}

Attached is a .zip of the sample project-folder.

140297–5124–$testwww_724.zip (1.06 MB)

You can’t expect WWW to finish if you sleep in your update thread.

Use a coroutine if you need to and ditch the sleep.

Here’s an example of how I do WWW which works in the player.

http://forum.unity3d.com/viewtopic.php?p=143202#142966

I do use a Coroutine:

   IEnumerator Sleep(float secs)
   {
      yield return StartCoroutine(SleepSecs(1));
   }

Perhaps the name of that method was a confusing choice.

I will look into your example code first thing tomorrow.

This right here is the wrong way to do it:

string Request (string url, float timeout)
   {
      Debug.Log("requesting from "+url);
      WWW www = new WWW(url);
      float stop = Time.realtimeSinceStartup+timeout;
      float current = Time.realtimeSinceStartup;
      while ((![url]www.isDone[/url])  (stop > Time.realtimeSinceStartup))
      {  ...

      }
    }

You should simply not do that. As along as you sit in that while loop isDone will never return true. You need to let the script engine come up for air in order for that process to update. You should use a coroutine to yield while the WWW operation is in progress. Pardon my C# idiocy, here’s an example in JavaScript:

function Request (string url, float timeout) {
  Debug.Log("requesting from "+url);
  var www = new WWW(url);
  yield www;
  // do whatever here
}

The WWW class works just fine in the web player, you just need to properly wait for its completion. :slight_smile:

Using a Coroutine to yield, while the WWW operation is in progress, is exactly, what I already do in the posted script. Actually the script works absolutely fine for all other plattforms than webplayer. Please pay attention that I call Sleep, not sleep, and Sleep is simply a function of the script, calling SleepSecs, where the Coroutine is used (I have to do it with this callstack, because I needed a caller of SleepSecs, which returnvalu is not used for something else, so I can yield the main routine, until the coroutine is completed like in the second c# sample here Unity yield documentation).

Just download the sample project for the issue, attached to the startpost, and try for yourself: It works just fine for all platforms except webplayer, but it definitely does not work for webplayer.

Perhaps it is an issue with 2.5, because it seemed to work in 2.1 (not sure, if it actually workes in 2.1, I did not test it myself).

You can yield directly on a WWW object (it’ll cast itself to an IEnumerator or whatever). This is the safest way to wait for a WWW operation to finish.

We use WWW a lot and have no issues with the web player.

Edit: Actually, you aren’t yielding at all. This isn’t yielding; you need the keyword:

      while ((![url]www.isDone[/url])  (stop > Time.realtimeSinceStartup))
      {
         if(current != Time.realtimeSinceStartup)
         {
            current = Time.realtimeSinceStartup;
            Debug.Log(stop + "   " + Time.realtimeSinceStartup);
         }
         Sleep(1); // <-- this isn't yielding!
      }

Im writing it the 3rd time now:
__S__leep is not __s__leep, but just another function in the script, the function actually doing the yield job.

I think you are mistaking coroutines for multithreading. The method containing the while loop has to contain the yield instruction. You can’t just call a subroutine and expect it to yield for the caller.

There is also no reason to do this as all.
The source given by HiggyB does all thats required. Coroutine that code and it will request the data and wait until the download is done.
There is no single reason to loop and check for www.done in the same script, as once it is done, the yield part in the coroutine will return and continue with the rest of the code in the coroutine.

the done check only makes sense in other scripts that for some reason are waiting for the data to be present but even that wouldn’t be a smart usage, as polling is never an option, its only a worst case handling scenario.
You would commonly let the downloading script inform them.

The yield is actually working fine.

I originally only wrote

yield return WWW;

but as this did wait in an endless loop in webplayer until I closed the browser task by force, because in webplayer the WWW seems to never asnwer at all, I wrote the script in the startpost, so the script stops trying after a few seconds and the browser does not hang up anymore.

No offense, but if you post broken code and ask for help on a forum you should consider the advice you get back. If you’re going to defend your admittedly broken code, what’s the point?

I will test the advices first thin tomottow morning back in office.
The simplest possible example, already not working is

yield return WWW;

The script above just demonstastrates that behavior without forcing anyone testing it, to restart his browser.
It is no broken code (otherwise it would not run just fine in editor (Unity on Windows and Mac and Unity iPhone Editor), in Windows standalone, in Mac standalone and on iPhone deivce.

You have to let the method return after constructing a WWW object.

WWW www = new WWW(url);

Unity event execution is not multithreaded, so waiting for www.isDone will never complete until your Update method completes a few times.

Your program is getting stuck at:

applicationMain

because of the same issue. You need to keep track of states with your MonoBehavior properties and let the events complete for stuff to happen.

[SOLVED]
The missing multithreading was the problem. I do now poll www.isDone every upDate frame and it runs fine now.

However, I had to implement the polling myself and coul not use yield www therefore, because .NET 1.1 does not support yield and the project is also used for iPhone (You can use yield directly in Unity scripts for iPhone but not in .NET 1.1 libraries build in Visual Studio, so it seems, the Mono version used for Unity iPhone device is a kind of “.NET 1.1++”, but because it does not support full .NET 2.0, external libs have to be build with Mono or with .NET 1.1 .).