Search Unity

WebGL and mobile cross platform uNet server

Discussion in 'UNet' started by Ashkan_gc, Dec 31, 2015.

  1. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Hi
    We are developing a game for both WebGL and Android and IOS, How can I setup a server which can listen to both normal UDP transport and WebSockets at the same time so mobile and WebGL players can play with each other?
     
    ariel-terrani likes this.
  2. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    @seanr This is for you probably man!
    I realized if I use another host using NetworkTransport.AddWebSocketHost() and add its connections to my network server using AddExternalConnection, then it should work if the main NetworkServer listens to normal UDP and extra host to web sockets, right?

    Am I doing it right? Is there any plan to support this out of the box? Should not bee too much work as I looked at HLAPI's source for someone knowing the codebase should take not much time.
     
  3. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Also is it possible to use WebSocket on mobile as well? We don't want to give advantage to mobile users simply by using UDP connections (i.e. better network perf)?
     
  4. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Well I worked more on it. Using the approach I suggested I can connect from webGL and normal mobile clients both to same server but connectionIDs clash. I'm trying to find a way to start IDs for one server from an offset number. Any help appreciated.
     
  5. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    @seanr I submitted this as a bug. If we have multiple sources for connections then their IDs are incremented separately so the external and internal connections will have ID clashes. Using both webGL and mobile together is crucial for our project so can you please provide some light on our darkness :)

    The bug's case number is 758614.

    I would like to know when this will be fixed and also is there any plan to automatically support a NetworkServer which listens to a websocket port and a UDP port at the same time for a cross platform game between webGL and Mobile.

    I looked at HLAPI's source to fix myself btw but ID assignment is in NetworkTransport as you know far better than I do.
     
    ariel-terrani likes this.
  6. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    right, this is currently not supported.

    If you are intending to use the relay server, websockets is not supported at all on the relay server either.
     
  7. DimensionU

    DimensionU

    Joined:
    Aug 1, 2013
    Posts:
    43
    Hi seanr,

    This seems like a trivial feature to add (keeping track of the connectionIds on the networkTransport). Not having it is a showstopper for us.

    We don't require the relay server, etc. Just the ability to have WebGL players playing along side players on other platforms.
     
    Last edited: Jan 5, 2016
    perevezentsev and Miscellaneous like this.
  8. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    maybe you could do it yourself. Run a websocket NetworkServerSimple alongside the regular server, and use a custom NetworkConnection class that intercepts the connectionId in Initialize and adds an offset to it.. then use NetworkServer.AddExternalConnection() to add the websocket connections to the game...
     
  9. DimensionU

    DimensionU

    Joined:
    Aug 1, 2013
    Posts:
    43
    Thanks! We'll give that a try.
     
  10. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    @seanr really a custom NetworkConnection can change the ID? Where am I supposed to do that? In ctor?

    And thanks for the fast reply. If this ID clash gets fixed then the rest is ok and I will use ExternalConnection as I described above.
     
  11. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    implement the virtual Initialize() function on NetworkConnection and modify the connection ID before calling into Initialize on the base class. It _should_ work, i have not tried it..

    examples of using custom connection classes:

    http://forum.unity3d.com/threads/unet-hlapi-sample-custom-connection-classes.375595/

    btw the connection array is a sparse array with connections at their ID index. so dont make the connection IDs very high, or that array will be padded out with many empty slots.
     
  12. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Many thanks man! I'll test and report back to you here. And yes I know about connections being an sparse array. I pretty much ate all of your docs and HLAPI's source just I overlooked this since I thought what NetworkTransport sets as ID can not be changed outside of it. For now I'm just a big mouth with suggestions and .. but later on when I get less busy , I might be able to help on HLAPI and send PRs to it. There are lacking features which are easy to implement and handy.
     
  13. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    @seanr It doesn't work and here is why

    The connectionID is sent to initialize is receiver from NetworkTransport.Connect (based on HLAPI code).
    No matter when I change it , it will cause the client to not be able to send data to server.
    The value is sent to client before I initialize server's NetworkConnection, I add the same offset both at client and at server. But when I try to continue sending data from client, it doesn't work.

    Do you think a fix is possible in upcoming patches? Just a way to set the ID after connection or set a starting offset for each NetworkSimpleServer.

    It's something which requires cooperation from NetworkTransport it seems.
     
  14. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    client's networkConnectionId is local to that client, not related at all to the server connectionId at all. Don't change it on the client, and see if that works. Client's don't know their connectionId on the server - or the connectionIds of other clients, and should not for security reasons.
     
  15. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    ah ok. when sending data from the server, the id does not match the transport layer. In:

    public virtual bool TransportSend(byte[] bytes, int numBytes, int channelId, out byte error)
    {
    return NetworkTransport.Send(hostId, connectionId, channelId, bytes, numBytes, out error);
    }


    you can override this and modify the connectionId that is passed to the base class to match the "real" connectionId..?
     
  16. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Nop it doesn't work!
    Server issues a warning about sending data to unprotected connection with id 11 (I add 10 as offset here)

    And then some errors about failure of sending data.

    upload_2016-1-5_21-22-33.png
     
  17. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Saw your second answer now,
    let me try that.
     
  18. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    I guess I need to compile my own Networking.dll because the override doesn't send the connectionID so I should modify NetworkConnection itself I guess (right)?
     
  19. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    connectionId is a member variable...

    public override bool TransportSend(byte[] bytes, int numBytes, int channelId, out byte error)
    {
    return NetworkTransport.Send(hostId, connectionId-10, channelId, bytes, numBytes, out error);
    }

    hmm.. but you would need to use the hostId of the websocket host too.. the serverHostId from the NetworkServerSimple instance
     
  20. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    @seanr
    Hey Sean! Many thanks man! It worked.
    It's awesome that you got this extension points in uNet from the beginning to empower users to do things without having to modify uNet and it's awesome that you released the source code as well.

    Last night I got very tired (the reason of my last dumm question :) ) so I finished it this morning.

    For reference for anyone who might want to do it.

    In order to be able to use an external connection in addition to main connection of NetworkServer (for using WebSocket and normal UDP socket together or any other purpose), I did these
    1- Create a NetworkServerSimple for the additional connection.
    2- In its MsgType.Connect add the connection to NetworkServer by using AddExternalConnection
    3- In MsgType.Disconnect remove the external connection (I'm not sure if it is required or not yet).
    4- To make it work you need to implement a custom NetworkConnection class which changes the connectionIDs for the external connections. uNet uses a combination of hostID + connectionID for sending data and each host increments its own connectionID numbers , if you want to use two hosts with NetworkServer, the IDs clash so you need to change the IDs for external connections for NetworkServer. Then the NetworkTransport layer needs its own original ID to be able to send the data. The result is a class like this


    Code (CSharp):
    1. public class WebGLCustomNetworkConnection : NetworkConnection
    2. {
    3.     private int offset;
    4.  
    5.     public override void Initialize(string networkAddress, int networkHostId, int networkConnectionId, HostTopology hostTopology)
    6.     {
    7.         offset = 10;
    8.         base.Initialize(networkAddress, networkHostId, networkConnectionId + offset, hostTopology);
    9.     }
    10.  
    11.     public override bool TransportSend(byte[] bytes, int numBytes, int channelId, out byte error)
    12.     {
    13.         return NetworkTransport.Send(CrossPlatformServerTest.secondServerHostID, connectionId - offset, channelId, bytes, numBytes, out error);
    14.     }
    15.  
    16. }
    Then before connecting using the NetworkSimpleServer you should set its NetworkConnection class by using SetNetworkConnectionClass<T>() generic method.

    The tricky point is to use the hostID of the NetworkSimpleServer (which I here simply put in a static variable) and not the hostID of NetworkServer.

    You can get the hostID after calling Listen from the hostID field of the NetworkServerSimple class.

    Hope it helps someone and thanks again Sean.
     
    andutoo and seanr like this.
  21. Zelek

    Zelek

    Joined:
    Jun 12, 2010
    Posts:
    87
    Thanks for including the steps and explanation, Ashkan.

    Sean, is this something that Unity might eventually include? I will eventually need this, as well as many other projects I imagine, and a more robust implementation that we could just toggle in the network manager (or something similar) would definitely be appreciated.
     
    Miscellaneous likes this.
  22. Miscellaneous

    Miscellaneous

    Joined:
    Sep 24, 2013
    Posts:
    53
    Would indeed be great to include, amazed it wasn't already natively supported...

    Unity please don't neglect this issue
     
  23. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Hopefully they will not, also for some unknown reason we got better network perf in WebGL by using the main NetworkServer for WebGL and the other server (SimpleNetworkServer) updated every frame for UDP.

    The reason is unknown to us but we sent an issue to unity technologies with our complete project.
     
  24. discordjb

    discordjb

    Joined:
    Jul 31, 2013
    Posts:
    3
    @Ashkan_gc
    I've tried your solution, but I am running into an issue where the MsgType handlers are not being called on the NetworkServerSimple instance.


    --Jeff
     
  25. discordjb

    discordjb

    Joined:
    Jul 31, 2013
    Posts:
    3
    @Ashkan_gc

    Here is my simple class I am testing with, it works as far as allowing the WebSocket and udp clients to connect, but again, the clients connecting to the NetworkServerSimple are not causing the registered handlers to be called even though they successfully connect:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4. using System.Collections;
    5. using System.Text.RegularExpressions;
    6. using System;
    7.  
    8.  
    9. public class MultiPlatformServer : MonoBehaviour {
    10.  
    11. NetworkServerSimple webGLServer = new NetworkServerSimple();
    12. public int port = 9000;
    13. public static int hostId = 0;
    14. public bool useWebSockets = true;
    15. public static MultiPlatformServer Instance;
    16. NetworkClient localClient;
    17. void Awake(){
    18. Instance = this;
    19. }
    20.  
    21. //Usethisfor initialization
    22. void Start () {
    23.  
    24. webGLServer.SetNetworkConnectionClass<WebGLCustomNetworkConnection>();
    25. webGLServer.useWebSockets = useWebSockets;
    26. webGLServer.RegisterHandler(MsgType.Connect, this.OnWebGLConnected);
    27. webGLServer.RegisterHandler(MsgType.Disconnect, this.OnWebGLDisconnected);
    28. webGLServer.Listen(port);
    29.  
    30.  
    31. print("Numhandlers:" + webGLServer.handlers.Count);
    32.  
    33. MultiPlatformServer.hostId = webGLServer.serverHostId;
    34.  
    35. NetworkServer.useWebSockets = !useWebSockets;
    36. NetworkServer.RegisterHandler(MsgType.Connect, OnConnected);
    37. NetworkServer.RegisterHandler(MsgType.Disconnect, OnDisconnected);
    38. NetworkServer.Listen(port);
    39.  
    40. print(string.Format("WebGLhostIdis{0}",webGLServer.serverHostId));
    41. print(string.Format("NonGLhostIdis{0}",NetworkServer.serverHostId));
    42.  
    43. localClient = new NetworkClient();
    44. localClient.Connect("192.168.100.103", 9000);
    45. }
    46.  
    47. //Updateiscalledonceper frame
    48. void Update () {
    49.  
    50. }
    51.  
    52. public void OnWebGLConnected(NetworkMessage netmsg){
    53. Debug.LogError("WebGLConnectionEstablished");
    54.  
    55. NetworkServer.AddExternalConnection(netmsg.conn);
    56. }
    57.  
    58. public void OnConnected(NetworkMessage netmsg){
    59. print("NonWebGLConnectionEstablished");
    60.  
    61. }
    62.  
    63. public void OnWebGLDisconnected(NetworkMessage netmsg){
    64. Debug.LogError("WebGLDisconnected");
    65. NetworkServer.RemoveExternalConnection(netmsg.conn.connectionId);
    66. }
    67. public void OnDisconnected(NetworkMessage netmsg){
    68. print("NonWebGLDisconnected");
    69. }
    70.  
    71. }
    72.  
     
    Last edited: Jun 2, 2016
  26. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    So do you mean the MsgType.Connect is not fired on the webGL client?
    In an Update method you should call the Update of the NetworkServerSimple every frame for it to pump messages and process them.

    Also after you add the connection as an external to NetworkServer, other messages will be processes by that.
     
  27. discordjb

    discordjb

    Joined:
    Jul 31, 2013
    Posts:
    3
    Thank you, calling the Update method in my Update was the key. I wasn't aware of that till now.
     
  28. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    There are problems with this approach regarding disconnecting and cleanly reconnecting. I'm trying to find out the issue.
     
  29. Gameccino

    Gameccino

    Joined:
    Aug 1, 2012
    Posts:
    40
    What is your approach to secure websocket connections?
    Serverside is trivial, should be easy to proxy it with Ngiinx, but I'm unsure how to say a webgl client to use a secure connection.
     
  30. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    You should either use javascript to use WebSockets from there or just use the BestHTTP package from asset store which supports secure websockets.
    If you mean using secure sockets in uNet. There is no way to do it at the moment.
     
  31. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    The disconnection/reconnection issue I was talking about was related to a bug unrelated to this about fragmented reliable channels which is fixed in 3.5.6p1
     
  32. Gameccino

    Gameccino

    Joined:
    Aug 1, 2012
    Posts:
    40
    oh, that's a very bad news!
     
  33. Gameccino

    Gameccino

    Joined:
    Aug 1, 2012
    Posts:
    40
    @Ashkan_gc
    I managed to realize the cross platform server as you indicated but right now I'm stuck on this issue:
    after a successful connection to the server and receiving packets from the client
    firing NetworkServer.AddPlayerForConnection (conn, player, 0) server side it will return
    "Channels not initialized sending on id '0"

    The webgl clent is not notificated and no message is sent to it. From what I undestand the NetworkServer can't send messages to the websocket client. I was wondering if I should use a method under the NetworkSimpleServer.


    Forget it, dumb error o the CustomNetworkConnection class, I commented out a base.initialize on that class days ago and forgot to decomment.

    Looks like it works well right now, doing more tests,
     
    Last edited: Aug 26, 2016
  34. sandy-macpherson

    sandy-macpherson

    Joined:
    Sep 25, 2015
    Posts:
    14
    Hi @Ashkan_gc - thanks for a very useful thread here. I've implemented a solution based on it and it mostly works, I have just run into a problem of clashing IDs on the server and don't yet know why it happened. The log showed
    "Empty player list given to NetworkServer.Destroy(), nothing to do." and after that the websocket simple server began its IDs at 101 again, resulting in clashes and players unable to connect. Any idea?

    Perhaps related is something I've noticed - the MsgType.Disconnect handler seems to never be called for the websocket server. I see above you expressed a doubt about whether it was necessary or not, just wondering if you have any more concrete info on it? For the record, the Disconnect event is fired for my primary server since the connection was added as an "ExternalConnection" and it seems to handle this fine, although the connectionId will be left dangling in the external connections list.
     
  35. dienat

    dienat

    Joined:
    May 27, 2016
    Posts:
    417
    I tried that but the client doesnt connect to server, i give hostid the value of 0. Also in the client i used startclient(), also tried localclient.connect you show without success. I am trying to connect client from editor using Unity 5.6
     
    Last edited: Jun 18, 2018
  36. Christian-Tucker

    Christian-Tucker

    Joined:
    Aug 18, 2013
    Posts:
    376
    For anyone that has been following along and is still lost, here's a working version of the script :)


    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using UnityEngine.UI;
    4. using System.Collections.Generic;
    5.  
    6.  
    7. public class StandaloneServerManager : MonoBehaviour {
    8.     public static int WebsocketHostId;
    9.  
    10.     private NetworkServerSimple websocketServer = new NetworkServerSimple();
    11.     private int port = 5050;
    12.  
    13.     private bool isReady = false;
    14.  
    15.     private void Start() {
    16.         NetworkTransport.Init();
    17.  
    18.         Debug.Log("Starting websocket server");
    19.         websocketServer.SetNetworkConnectionClass<WebsocketConnection>();
    20.         websocketServer.useWebSockets = true;
    21.         websocketServer.RegisterHandler(MsgType.Connect, OnWebsocketConnection);
    22.         websocketServer.RegisterHandler(MsgType.Disconnect, OnWebsocketDisconnect);
    23.         websocketServer.Listen(port);
    24.         Debug.Log("Started websocket server");
    25.  
    26.         StandaloneServerManager.WebsocketHostId = websocketServer.serverHostId;
    27.  
    28.         Debug.Log("Starting binary server");
    29.         NetworkServer.useWebSockets = false;
    30.         NetworkServer.RegisterHandler(MsgType.Connect, OnBinaryConnection);
    31.         NetworkServer.RegisterHandler(MsgType.Disconnect, OnBinaryDisconnect);
    32.         NetworkServer.Listen(port);
    33.         Debug.Log("Started binary server");
    34.  
    35.         Debug.Log("Server's successfully started on port: " + (port));
    36.         isReady = true;
    37.     }
    38.  
    39.     private void Update() {
    40.         if (!isReady) return;
    41.  
    42.         // Call the websocket server's update function so that websocket packets
    43.         // can also be processed.
    44.         websocketServer.Update();
    45.  
    46.     }
    47.  
    48.  
    49.     #region Binary Server
    50.     void OnBinaryConnection(NetworkMessage packet) {
    51.         Debug.Log("Binary Client Connected.");
    52.     }
    53.  
    54.     void OnBinaryDisconnect(NetworkMessage packet) {
    55.         Debug.Log("Binary client disconnected.");
    56.     }
    57.     #endregion
    58.  
    59.     #region Websocket Server
    60.     void OnWebsocketConnection(NetworkMessage packet) {
    61.         Debug.Log("Websocket Client Connected.");
    62.         NetworkServer.AddExternalConnection(packet.conn);
    63.     }
    64.  
    65.     void OnWebsocketDisconnect(NetworkMessage packet) {
    66.         Debug.Log("Websocket Client disconnected.");
    67.         NetworkServer.RemoveExternalConnection(packet.conn.connectionId);
    68.     }
    69.     #endregion
    70. }
    71.  
    and...

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Networking;
    5.  
    6. public class WebsocketConnection : NetworkConnection {
    7.     private int offset;
    8.  
    9.     public override void Initialize(string networkAddress, int networkHostId, int networkConnectionId, HostTopology hostTopology) {
    10.         offset = 10;
    11.         base.Initialize(networkAddress, networkHostId, networkConnectionId + offset, hostTopology);
    12.     }
    13.  
    14.     public override bool TransportSend(byte[] bytes, int numBytes, int channelId, out byte error) {
    15.         return NetworkTransport.Send(StandaloneServerManager.WebsocketHostId, connectionId - offset, channelId, bytes, numBytes, out error);
    16.     }
    17.  
    18. }
     
    Last edited: Jul 25, 2018
    Ashkan_gc, bartofzo and andutoo like this.
  37. bartofzo

    bartofzo

    Joined:
    Mar 16, 2017
    Posts:
    151
    Thanks for this thread!

    Question regarding the offset. I'm developing a game where up to a 100 players can be connected to a server simultaneously. Would it be safe to set the offset to a large value of say 1000? So that the chance of an ID clashing is minimal. I've tested this and it works, but I don't know if I can expect any side effects from it.
     
  38. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    As far as I know it should be fine.
     
  39. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    sorry for late reply, haven't done something similar for a while but I guess custom connection allowed you to find out about disconnects and now they were not fired for websocket/externial/secondary connections
     
  40. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Awesome
     
  41. bartofzo

    bartofzo

    Joined:
    Mar 16, 2017
    Posts:
    151
    Thanks. Went for a different approach though. I just used 2 NetworkServerSimple instances and built a wrapper class around it that makes it seem like one server. Works without the ID clashes and I don't need the extra functionality of the static NetworkServer.