Search Unity

Code works outside of Unity, but inside of it something gets lost...

Discussion in 'Scripting' started by detlion1643, Oct 5, 2015.

  1. detlion1643

    detlion1643

    Joined:
    Jul 26, 2012
    Posts:
    13
    I have made a framework .dll that encapsulates a lot of networking stuff. I use it outside of Unity without any issues at all. However, adding it to Unity (inside a Plugins folder) works, but only until I try sending messages back/forth.

    The gist of it is that the Login script runs, handling events from the ServerManager that it needs (connections/messages). The messages are a custom class from my framework. Now, the connections work great, as I am connected. I also get the first message from the server that it will now accept messages, so it correctly turns the login button on. However, after clicking the login button (which calls the LoginClick), I do not get anything back from the server. No message is returned, and no message from the server can come through anymore (including the previous working "accepting messages").

    The login function correctly works, as verified by debugging the received message on the server side. It correctly finds the information in our database, and writes back the correct message.

    The odd thing about this, it all works when I made a test Windows Form application. Using the same ServerManager approach, I made a WinForm with the same design and textboxes, and even the login call on a button click. It works, and I even get the message back ("AccountLogin") from the server properly.

    So, it can't really be an issue with my framework/dll, could it be? I mean the whole thing works, then just breaks after calling the login function on a Unity button click. The exact same scenario works in WinForms. Unity even gets all messages I send from the server until I click that button to try logging in.

    ServerManager.cs:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using RiptideShared;
    5. using RiptideShared.Messaging;
    6. using System.Collections.Generic;
    7.  
    8. public static class ServerManager
    9. {
    10.  
    11.    private static RiptideClient _client = null;
    12.  
    13.   public delegate void OnConnectionChangedEventHandler(RiptideClient Sender, eConnectionStatus ConnectionStatus);
    14.   public static event OnConnectionChangedEventHandler OnConnectionChanged;
    15.   public delegate void OnMessageReceivedEventHandler(RiptideClient Sender, Message Message);
    16.   public static event OnMessageReceivedEventHandler OnMessageReceived;
    17.  
    18.    public static bool ClientConnected
    19.    {
    20.      get
    21.      {
    22.        if(_client.ConnectionStatus == RiptideShared.eConnectionStatus.Connected)
    23.          return true;
    24.        else
    25.          return false;
    26.      }
    27.    }
    28.  
    29.   public static RiptideClient Client { get { return _client; } }
    30.  
    31.    public static void Start()
    32.    {
    33.   if (_client == null)
    34.   {
    35.   _client = new RiptideShared.RiptideClient();
    36.   _client.OnConnectionChanged += HandleOnConnectionChanged;
    37.   _client.OnMessageReceived += HandleOnMessageReceived;
    38.   }
    39.      _client.Connect ();
    40.    }
    41.  
    42.    static void HandleOnConnectionChanged (RiptideClient Sender, eConnectionStatus ConnectionStatus)
    43.    {
    44.   Debug.Log(ConnectionStatus.ToString());
    45.   if (OnConnectionChanged != null)
    46.   OnConnectionChanged(Sender, ConnectionStatus);
    47.    }
    48.  
    49.    static void HandleOnMessageReceived (RiptideClient Sender, Message Message)
    50.    {
    51.   Debug.Log(Message.MessageType);
    52.   if (OnMessageReceived != null)
    53.   OnMessageReceived(Sender, Message);
    54.    }
    55.  
    56.   public static void Login(string Username, string Password)
    57.   {
    58.   _client.WriteTo(new Message(null, Message.MessageTypes.AccountLogin, new List<object>() { Username, Password }));
    59.   }
    60.  
    61. }
    Login.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System.Collections;
    5. using RiptideShared.Messaging;
    6. using RiptideShared;
    7. using System.Collections.Generic;
    8.  
    9. public class Login : MonoBehaviour {
    10.  
    11.   Text _connectionStatusLabel;
    12.   InputField _emailInputField;
    13.   InputField _passwordInputField;
    14.   Button _loginButton;
    15.  
    16.   bool _serverAcceptingMessages = false;
    17.   RiptideShared.eConnectionStatus connectionStatus = eConnectionStatus.NotConnected;
    18.  
    19.    // Use this for initialization
    20.    void Start () {
    21.  
    22.   //get the variables filled up
    23.   _connectionStatusLabel = (Text)GameObject.Find("ConnectionStatusLabel").GetComponent(typeof(Text));
    24.   _emailInputField = (InputField)GameObject.Find("EmailInput").GetComponent(typeof(InputField));
    25.   _passwordInputField = (InputField)GameObject.Find("PasswordInput").GetComponent(typeof(InputField));
    26.   _loginButton = (Button)GameObject.Find("LoginButton").GetComponent(typeof(Button));
    27.  
    28.   //start the server manager
    29.   ServerManager.OnConnectionChanged += ServerManager_OnConnectionChanged;
    30.   ServerManager.OnMessageReceived += ServerManager_OnMessageReceived;
    31.   ServerManager.Start();
    32.    }
    33.  
    34.   void ServerManager_OnMessageReceived(RiptideClient Sender, Message Message)
    35.   {
    36.   switch(Message.MessageType)
    37.   {
    38.   case Message.MessageTypes.ServerAcceptingMessages:
    39.   _serverAcceptingMessages = true;
    40.   break;
    41.   case Message.MessageTypes.AccountLogin:
    42.   break;
    43.   }
    44.   }
    45.  
    46.   void ServerManager_OnConnectionChanged(RiptideClient Sender, eConnectionStatus ConnectionStatus)
    47.   {
    48.   connectionStatus = ConnectionStatus;
    49.   }
    50.    
    51.    // Update is called once per frame
    52.    void Update () {
    53.   _connectionStatusLabel.text = "Connection Status: " + connectionStatus.ToString();
    54.   if ((!_loginButton.interactable) && (connectionStatus == eConnectionStatus.Connected) && (_serverAcceptingMessages))
    55.   _loginButton.interactable = true;
    56.   else if (connectionStatus != eConnectionStatus.Connected)
    57.   _loginButton.interactable = false;
    58.    }
    59.  
    60.   public void LoginClick()
    61.   {
    62.   ServerManager.Login(_emailInputField.text, _passwordInputField.text);
    63.   }
    64.  
    65. }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Just a random thought is if you are running this in the browser: the browser has a sandbox model that prohibits Unity's code from contacting third party servers, unless there is an appropriate cross-site XML policy file in place.
     
  3. detlion1643

    detlion1643

    Joined:
    Jul 26, 2012
    Posts:
    13
    Browser as in the web? No, this happens in the editor when "playing" and also on a build of the .exe (through Unity) running standalone.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Yeah, I meant in the webplayer build just because that's a common pitfall, forgetting about XSS.

    Other than that you'll probably have to wireshark it or otherwise sniff the traffic in a way that can reveal where you're going astray... double check your target URIs, etc.
     
  5. detlion1643

    detlion1643

    Joined:
    Jul 26, 2012
    Posts:
    13
    Bringing this thread up to the top again.

    I finally had some time to download wireshark. Ugh, I can barely understand the things going on. But, I did run the server and client through Unity while it was capturing the packets. Looking through what was displayed, I could not find any entry at all related to the port I use for the server/client.

    This leads me to believe that wireshark is useless (for the time being) as the server/client run on the same machine. The server has been tested, and connected to successfully, from outside the local network, so hopefully I can get someone to test the Unity program for me while I have the server and wireshark running on my machine.

    As another test, I added a button into the Unity program to send a message to the server. Verified it correctly sends the message to the server, and the server sends a response back. Unity receives this message, as verified through my receiving message event and showing a Debug.Log(***).

    Now, this really confuses me since I now know that I can send a message and get one back.

    So, I dug a little deeper. It turns out that in my Login procedure on the server, I do some things that apparently causes issues. I found 1 lines which if I comment them out, Unity gets the Login message back correctly. However, by not running the "issue" line, the server doesn't think that an account is logged in.

    So, why is it that the WinForms application works correctly (with the "issue" line) and the Unity one doesn't? I don't know, and probably no one else knows either. It's such a weird issue.

    Here is my login procedure (on the server):
    Code (csharp):
    1.  
    2. private void AccountLogin(ref RiptideClient Client, Message Message)
    3.   {
    4.   int id = -1;
    5.   id = AccountLoginID(Message);
    6.  
    7.   if (id > 0)
    8.   {
    9.   //successful login
    10.   AccountInformation(id);
    11.   Player player = PlayerInformation(id);
    12.   Client.Player = player;
    13.  
    14.   Server.Log("Login: " + _username);
    15.   RiptideShared.Messaging.Message message = new RiptideShared.Messaging.Message(Client.Player, RiptideShared.Messaging.Message.MessageTypes.AccountLogin,
    16.   new List<object>() { id, Client.Player });
    17.   Client.WriteTo(message);
    18.   ClientManager.SendToAccountType("Developer", message);
    19.   }
    20.   else
    21.   {
    22.   //can't login
    23.  
    24.   }
    25.   }
    The "Client.Player = player" is the 1 line that doesn't let Unity get the message back. Commenting it out, Unity receives the message back. Remember, it works "as is" in a WinForms application.

    I think my head hurts now :(
     
  6. detlion1643

    detlion1643

    Joined:
    Jul 26, 2012
    Posts:
    13
    I know this is a long post, so please bare with it if you decide to read it.

    It turns out the "Client.Player = player" isn't exactly the issue, it just looked like it.

    If I replace
    Code (csharp):
    1.  
    2. RiptideShared.Messaging.Message message = new RiptideShared.Messaging.Message(Client.Player, RiptideShared.Messaging.Message.MessageTypes.AccountLogin,
    3.   new List<object>() { id, Client.Player });
    with this
    Code (csharp):
    1.  
    2. RiptideShared.Messaging.Message message = new RiptideShared.Messaging.Message(null, RiptideShared.Messaging.Message.MessageTypes.AccountLogin,
    3.   new List<object>() { id, null });
    Unity can receive the message back.

    However, if I try 1 of the "Client.Player" variables instead of null, Unity doesn't receive the message back. It's important that I send the Client.Player back to the client that is logging in.

    Here is my Message class:
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6. using RiptideShared.Player;
    7.  
    8. namespace RiptideShared.Messaging
    9. {
    10.   [Serializable]
    11.   public class Message
    12.   {
    13.  
    14.   public enum MessageTypes
    15.   {
    16.   *** enums removed, have about 50 of them so far... ***
    17.   }
    18.  
    19.   private MessageTypes messageType;
    20.   private List<object> messageParameters = new List<object>();
    21.   private Player.Player _player;
    22.  
    23.   public MessageTypes MessageType { get { return messageType; } }
    24.   public List<object> MessageParameters { get { return messageParameters; } }
    25.   public Player.Player Player { get { return _player; } }
    26.  
    27.   public Message(Player.Player Player, MessageTypes MessageType)
    28.   {
    29.   _player = Player;
    30.   messageType = MessageType;
    31.   messageParameters = new List<object>() { };
    32.   }
    33.  
    34.  
    35.   public Message(Player.Player Player, MessageTypes MessageType, List<object> MessageParameters)
    36.   {
    37.   _player = Player;
    38.   messageType = MessageType;
    39.   messageParameters = MessageParameters;
    40.   }
    41.  
    42.   }
    43. }
    Every class used inside of Player (and sub classes) are all correctly marked [Serializable] so they can be passed via network. I won't put all of those in here unless absolutely necessary.

    Here is my DataWriter:
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.IO;
    5. using System.Linq;
    6. using System.Net.Sockets;
    7. using System.Runtime.Serialization.Formatters.Binary;
    8. using System.Text;
    9.  
    10.  
    11. namespace RiptideShared
    12. {
    13.   class DataWriter
    14.   {
    15.  
    16.   //declarations
    17.   private NetworkStream stream;
    18.  
    19.   public DataWriter(RiptideClient Client)
    20.   {
    21.   stream = Client.Stream;
    22.   }
    23.  
    24.   private byte[] Combine(byte[] one, byte[] two)
    25.   {
    26.   byte[] ret = new byte[one.Length + two.Length];
    27.   Buffer.BlockCopy(one, 0, ret, 0, one.Length);
    28.   Buffer.BlockCopy(two, 0, ret, one.Length, two.Length);
    29.   return ret;
    30.   }
    31.  
    32.   public void WriteTo(RiptideShared.Messaging.Message Message)
    33.   {
    34.   BinaryFormatter bf = new BinaryFormatter();
    35.   MemoryStream ms = new MemoryStream();
    36.   bf.Serialize(ms, Message);
    37.  
    38.   byte[] buffer = ms.ToArray();
    39.   //prepend the length of the buffer
    40.   byte[] toPrepend = Encoding.ASCII.GetBytes("[" + buffer.Length.ToString() + "]");
    41.   byte[] combined = Combine(toPrepend, buffer);
    42.  
    43.   stream.Write(combined, 0, combined.Length);
    44.   }
    45.  
    46.   }
    47. }
    And my DataReader:
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6.  
    7. using System.IO;
    8. using System.Runtime.Serialization.Formatters.Binary;
    9. using System.Net.Sockets;
    10. using System.Diagnostics;
    11.  
    12. namespace RiptideShared
    13. {
    14.   class DataReader
    15.   {
    16.  
    17.   //events
    18.   public delegate void OnMessageReceivedEventHandler(Messaging.Message Message);
    19.   public event OnMessageReceivedEventHandler OnMessageReceived;
    20.  
    21.   //declarations
    22.   RiptideClient client; //this is the exact copy of the client passed in (on server = from _sharedClients, on client = from client)
    23.   NetworkStream stream;
    24.   byte[] constantBuffer = new byte[0];
    25.  
    26.   //methods
    27.   public DataReader(RiptideClient Client)
    28.   {
    29.   client = Client;
    30.   stream = Client.Stream;
    31.   }
    32.  
    33.   public void StartReading()
    34.   {
    35.   WaitForData();
    36.   }
    37.  
    38.   private void WaitForData()
    39.   {
    40.   byte[] buffer = new byte[client.GetBufferSize];
    41.   stream.BeginRead(buffer, 0, buffer.Length, ReadData, buffer);
    42.   }
    43.  
    44.   private void ReadData(IAsyncResult ar)
    45.   {
    46.   //try
    47.   //{
    48.   int read = -1;
    49.   try
    50.   {
    51.   read = stream.EndRead(ar);
    52.   }
    53.   catch(Exception ex)
    54.   {
    55.   //ungraceful disconnect???
    56.   //RiptideShared.Messaging.Message message = new Messaging.Message(client.Player, Messaging.Message.MessageTypes.AccountDisconnect);
    57.   //if (OnMessageReceived != null)
    58.   //  OnMessageReceived(message);
    59.   //return;
    60.   }
    61.    
    62.   if (read == 0)
    63.   {
    64.   //client disconnected
    65.  
    66.   }
    67.   else
    68.   {
    69.   byte[] buffer = ar.AsyncState as byte[];
    70.   constantBuffer = Combine(constantBuffer, buffer, read);
    71.   string checkLength = Encoding.Default.GetString(constantBuffer, 0, constantBuffer.Length);
    72.   while (true)
    73.   {
    74.   if ((checkLength.Contains("[")) && (checkLength.Contains("]")))
    75.   {
    76.   string toCheck = checkLength.Substring(1, checkLength.IndexOf("]") - 1);
    77.   int lengthOfBytes = Convert.ToInt32(checkLength.Substring(1, checkLength.IndexOf("]") - 1));
    78.   if (constantBuffer.Length >= lengthOfBytes)
    79.   {
    80.   //have an instance of our class here
    81.   byte[] toPrepend = Encoding.ASCII.GetBytes("[" + lengthOfBytes.ToString() + "]");
    82.   byte[] messageClass = new byte[lengthOfBytes];
    83.   Buffer.BlockCopy(constantBuffer, toPrepend.Length, messageClass, 0, lengthOfBytes);
    84.  
    85.   RiptideShared.Messaging.Message Message = null;
    86.   using (MemoryStream memStream = new MemoryStream())
    87.   {
    88.   BinaryFormatter binForm = new BinaryFormatter();
    89.   //memStream.Write(buffer, 0, buffer.Length);
    90.   memStream.Write(messageClass, 0, messageClass.Length);
    91.   memStream.Seek(0, SeekOrigin.Begin);
    92.   Message = (RiptideShared.Messaging.Message)binForm.Deserialize(memStream);
    93.   if (OnMessageReceived != null)
    94.   OnMessageReceived(Message);
    95.   }
    96.  
    97.   //reset the constantBuffer to everything minus the just used message class
    98.   int constantLength = constantBuffer.Length;
    99.   int prependLength = toPrepend.Length;
    100.   int messageLength = messageClass.Length;
    101.   int togetherLength = prependLength + messageLength;
    102.   int newLength = constantLength - togetherLength;
    103.   byte[] newByte = new byte[newLength];
    104.   Buffer.BlockCopy(constantBuffer, togetherLength, newByte, 0, newLength);
    105.   constantBuffer = newByte;
    106.  
    107.   checkLength = Encoding.Default.GetString(constantBuffer, 0, constantBuffer.Length);
    108.   }
    109.   else
    110.   break;
    111.   }
    112.   else
    113.   break;
    114.   }
    115.   }
    116.   WaitForData();
    117.   //}
    118.   //catch(Exception ex)
    119.   //{
    120.   //ungraceful disconnect
    121.    
    122.   //}
    123.   }
    124.  
    125.   private byte[] Combine(byte[] one, byte[] two, int twoRead)
    126.   {
    127.   byte[] ret = new byte[one.Length + twoRead];
    128.   Buffer.BlockCopy(one, 0, ret, 0, one.Length);
    129.   Buffer.BlockCopy(two, 0, ret, one.Length, twoRead);
    130.   return ret;
    131.   }
    132.  
    133.   }
    134. }