Photon Server With Unity And The UI System

deadstar
deadstar
edited July 2015 in DotNet
It occurs to me that this post is better off in the DotNet section rather than the Server section, so deleted the old post and reposted it here.

I'm currently learning how to use Photon On Premises Server with Unity. I've been working through the tutorials from CJR Gaming (Which is probably a bit advanced for my current plans) and am also trying to modify some of the standard Photon tutorials to work with Unity.

I've started on the "An App From Scratch" tutorial and so far it has gone well....Except I'm using Unity 5's UI system instead of OnGUI. I'm trying to display the chat messages in a Text element, showing a history of the message between the two users (as a standard graphic chat system would). However, if I try to add the received message into the Text element, I receive a null reference exception.

This issue occurs whenever I try to do this from within any of the Photon specific functions (in this case, "OnEvent", or if I wanted to have a text element displaying the current connection status using the "OnStatusChanged" function). The only way I've found around it so far is to tell the OnEvent function through code which Text element I want it to add the messages to at the beginning of the statement. Here's a snippet of the code I'm using;
public GameObject ChatHistory;

public void OnEvent (EventData eventData)
		{
			ChatHistory = GameObject.FindGameObjectWithTag ("TextField");
			Debug.Log ("Event: " + eventData.Code);
			if (eventData.Code == 1)
			{
				Debug.Log ("Chat: " + eventData.Parameters[1]);
				if (ChatHistory.GetComponent<Text>().text == "")
					ChatHistory.GetComponent<Text>().text = eventData.Parameters[1].ToString ();
				else
					ChatHistory.GetComponent<Text>().text = ChatHistory.GetComponent<Text>().text + "\n" + eventData.Parameters[1].ToString ();
			}
		}

And here's the full script;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using ExitGames.Client.Photon;
using Hashtable = ExitGames.Client.Photon.Hashtable;

namespace ChatServer
{
	public class ChatClient : MonoBehaviour, IPhotonPeerListener
	{
		private bool _connected;
		private ChatClient _client;
		private PhotonPeer _peer;

		public InputField ChatText;
		public GameObject ChatHistory;

		void Start ()
		{
			Application.runInBackground = true;

			_client = new ChatClient ();
			_peer = new PhotonPeer (_client, ConnectionProtocol.Udp);

			_client._connected = false;

			_peer.Connect ("127.0.0.1:5055", "ChatServer");
		}

		void Update ()
		{
			if (!_client._connected)
			{
				_peer.Service ();
			}

			if (_client._connected)
			{
				_peer.Service ();

				if (Input.GetKeyUp (KeyCode.Return))
				{
					var parameters = new Dictionary <byte, System.Object> { { 1, ChatText.text.ToString () } };
					_peer.OpCustom (1, parameters, true);
					ChatText.text = "";
				}
			}
		}

		public void DebugReturn (DebugLevel level, string message)
		{
			Debug.Log (level + ": " + message);
		}

		public void OnEvent (EventData eventData)
		{
			ChatHistory = GameObject.FindGameObjectWithTag ("TextField");
			Debug.Log ("Event: " + eventData.Code);
			if (eventData.Code == 1)
			{
				Debug.Log ("Chat: " + eventData.Parameters[1]);
				if (ChatHistory.GetComponent<Text>().text == "")
					ChatHistory.GetComponent<Text>().text = eventData.Parameters[1].ToString ();
				else
					ChatHistory.GetComponent<Text>().text = ChatHistory.GetComponent<Text>().text + "\n" + eventData.Parameters[1].ToString ();
			}
		}

		public void OnOperationResponse (OperationResponse operationResponse)
		{
			Debug.Log ("Response: " + operationResponse.OperationCode);
		}

		public void OnStatusChanged (StatusCode statusCode)
		{
			if (statusCode == StatusCode.Connect)
			{
				this._connected = true;
			}
			else if (statusCode == StatusCode.Disconnect)
			{
				this._connected = false;
			}
		}

		public void OnApplicationQuit ()
		{
			_peer.Disconnect ();
		}
	}
}

If anyone could let me know if I'm missing something or if this is just how it has to be done, that'd be great.

Comments

  • Good to read you are making some progress with the "App From Scratch" doc.

    The NullReferenceException should usually point you to the line of code which causes it. Reading that line carefully will help you find what variable can be null and then you need to figure out why that is. In doubt, log the various values with Debug.Log() and see which one is null.
    We can't currently help with custom UI code, sorry.

    This might help:
    https://www.youtube.com/watch?v=TRLsmuYMs8Q
    https://www.youtube.com/watch?v=_ijhS6aezQc

    Let us know if you find a better video.
  • If only it were that simple an issue :)

    The problem is that no matter what I attempt to assign the text element to, if it is within one of the Photon functions such as OnEvent or OnStatusChanged, it gives a null reference exception.

    If I was to use the Update function to check _connected and change the text element on that, then it would work;
    void Update ()
    {
                    if (_connected)
                                    ChatHistory.GetComponent<Text>().text = "Connected";
                    else
                                    ChatHistory.GetComponent<Text>().text = "Disconnected";
    }
    

    But if I try to assign the text from within the OnEvent or OnStatusChanged functions, even if it's just something random I wrote in for testing, I get a null reference exception;
    public void OnEvent (EventData eventData)
          {
             ChatHistory = GameObject.FindGameObjectWithTag ("TextField");
             Debug.Log ("Event: " + eventData.Code);
             if (eventData.Code == 1)
             {
                Debug.Log ("Chat: " + eventData.Parameters[1]);
                
    			ChatHistory.GetComponent<Text>().text = "This won't throw an exception because I've defined it in the first line of the OnEvent function";
    			
             }
          }
    
    public Text ConnectionStatus;
    
    public void OnStatusChanged (StatusCode statusCode)
          {
             if (statusCode == StatusCode.Connect)
             {
                this._connected = true;
    			ConnectionStatus.GetComponent<Text>().text = "This will throw an exception because I haven't defined what ConnectionStatus is through code within this function";
             }
             else if (statusCode == StatusCode.Disconnect)
             {
                this._connected = false;
             }
          }
    

    Whatever the issue is, it appears to be within the built in Photon functions interacting with the Unity UGUI objects. Unless these functions are told through code exactly which element is assigned, they throw a null reference exception.

    I'd really prefer not to have to define it each time as it eliminates some of the flexibility behind the UGUI system.
  • Did you find out what is null? ConnectionStatus or the result of GetComponent<Text>?
    I would like to see your call stack. Maybe the issue is related to how we call OnStatusChanged. It might be off Unity's main thread (even though I would expect it in there).
  • The call stack I get when receiving a message (so during the OnEvent method when I use ChatHistory.GetComponent<Text>().text = eventData.Parameters[1].ToString (); ) is;
    System.NullReferenceException: Object reference not set to an instance of an object
    at ChatServer.ChatClient.OnEvent (ExitGames.Client.Photon.EventData eventData) [0x0004b] in C:\Users\Michael\Documents\Unity Projects\MyServer\Assets\ChatServer\ChatClient.cs:65
    at ExitGames.Client.Photon.PeerBase.DeserializeMessageAndCallback (System.Byte[] inBuff) [0x00000] in <filename unknown>:0
    at ExitGames.Client.Photon.EnetPeer.DispatchIncomingCommands () [0x00000] in <filename unknown>:0
    at ExitGames.Client.Photon.PhotonPeer.DispatchIncomingCommands () [0x00000] in <filename unknown>:0
    at ExitGames.Client.Photon.PhotonPeer.Service () [0x00000] in <filename unknown>:0
    at ChatServer.ChatClient.Update () [0x0002b] in C:\Users\Michael\Documents\Unity Projects\MyServer\Assets\ChatServer\ChatClient.cs:41

    The ChatHistory object has been set in the editor and I can use it in the standard Unity methods such as Start and Update, so that's not where the null reference comes in.
    The eventData.Parameters[1] shows perfectly fine in the Debug.Log when it prints to the console, so that's not where it comes in either.

    Only if I include the line ChatHistory = GameObject.FindGameObjectWithTag ("TextField"); or some other variation of defining within the OnEvent method what the ChatHistory object actually is will it work if I'm using one of the built in Photon Methods.
  • Can you check if Start() got called before OnEvent() gets called for the first time?
    Log it out and check the order of things.

    I don't get any closer to understanding the cause of this. Can you zip your project (or a sample) and share it with us (via dropbox or so)? Send a mail to developer@exitgames.com so we can check it.
  • The start function is definitely being called before OnEvent.

    I've uploaded the Unity Files and Photon Files to drop box.

    Just unzip the Unity files into the Asset folder of a project and open the ChatServer scene (within the scenes folder) and everything should be setup there.

    Unzip the Photon files into the Deploy folder, run the Photon Control and use the ChatServer application listed there.

    The error specifically appears on the receiving chat client (which I usually run through the Unity editor and have a PC built version running for the sending chat client).
  • You forgot to include some apps and resources in the server zip your sent. Had to add those.
    The assets were OK and I could find and fix your issues.

    You need to fix the warnings that Unity gives you!
    If you create your ChatClient class with "new", it will not work properly as MonoBehaviour.
    In Start(), the client exists. It does not need to be created again with new ChatClient.

    Also, you can define the type of your ChatHistory as Text class. If you assign the GameObject to the field with that definition, the ChatHistory field will directly relate to the component you want to use. The amount of GetComponent you used is probably not good at runtime but might be due to debugging this.
  • By the way: If you build your own Chat Server, you should tell us! It might be mixed up with our own solution:
    www.exitgames.com/en/Chat
  • Perfect, never had issues with that warning before so I wasn't worried about it.

    And yes, the GetComponent was strictly used as a debugging method to ensure I wasn't missing something.

    As for building my own ChatServer, for now it's just a way of learning more about building custom logic for Photon.

    Thanks for your assistance Tobias!
  • You're welcome :)