Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Manual connection vs. Master Server

Discussion in 'Multiplayer' started by burnumd, Feb 13, 2009.

  1. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    Hi all,

    I'm trying to create a networked scene and I'm finding that connecting when using the Unity-provided MasterServer works most of the time, but if I try to allow the client to manually enter in an ip address and port, the connection fails using the same call, even if I enter in the data as it appears in the host list. Is there something to do with parsing strings that I might be doing wrong? Any advice would be appreciated. I've included the client connection code below:
    Code (csharp):
    1. function OnGUI () {
    2.     var myPeerType : NetworkPeerType = Network.peerType;
    3.     if (myPeerType == NetworkPeerType.Connecting) {
    4.         GUI.Box(Rect(Screen.width / 2 - 100, Screen.height / 2 - 10, 200, 20),
    5.                 "Connecting to server...");
    6.     }
    7.     else if (myPeerType == NetworkPeerType.Disconnected) {
    8.         if (GUILayout.Button("Refresh")) {
    9.             hostList = MasterServer.PollHostList();
    10.         }
    11.         if (hostList  hostList.length != 0) {
    12.             for (var host : HostData in hostList) {
    13.                 GUILayout.BeginHorizontal();
    14.                     GUILayout.Label("Host: IP: ");
    15.                     for (var ipPart : String in host.ip) {
    16.                         GUILayout.Label(ipPart + ".");
    17.                     }
    18.                     GUILayout.Label(", port: " + host.port + ", use NAT? " + host.useNat);
    19.                     if (GUILayout.Button("Connect")) {
    20.                         Network.useNat = host.useNat;
    21.                         var logString2 = "Connecting to ";
    22.                         for (var ipPart : String in host.ip) {
    23.                             logString2 += ipPart;
    24.                             logString2 += ".";
    25.                         }
    26.                         logString2 += ", port: " + host.port + ", use NAT: " + Network.useNat;
    27.                         Debug.Log(logString2);
    28.    
    29.                         Network.Connect(host.ip, host.port);
    30.                     }
    31.                 GUILayout.EndHorizontal();
    32.             }
    33.         }
    34.         GUILayout.Label("");
    35.         GUILayout.Label("Connect to:");
    36.         IPEntry();
    37.         PortEntry();
    38.         Network.useNat = GUILayout.Toggle(Network.useNat, "Use NAT?");
    39.         if (GUILayout.Button("Connect with these settings")) {
    40.             try {
    41.                 var logString = "Connecting to ";
    42.                 for (var ipPart : String in remoteAddress) {
    43.                     logString += ipPart;
    44.                     logString += ".";
    45.                 }
    46.                 logString += ", port: " + remotePort + ", use NAT: " + Network.useNat;
    47.                 Debug.Log(logString);
    48.                 var connectionStatus : NetworkConnectionError = Network.Connect(remoteAddress, remotePort);
    49.             }
    50.             catch (err) {
    51.                 Debug.LogError(err);
    52.                 GUILayout.Label("Could not connect: " + err);
    53.             }
    54.         }
    55.     }
    56. }
    57.  
    58. function IPEntry () { //GUI-only function
    59.     GUILayout.BeginHorizontal();
    60.         GUILayout.Label("IP address: ");
    61.         for (var i : int = 0 ; i < remoteAddress.length; i++) {
    62.             remoteAddress[i] =
    63.                 GUILayout.TextField(remoteAddress[i], 3, GUILayout.Width(30));
    64.             if (i != remoteAddress.length - 1) {
    65.                 GUILayout.Label(".");
    66.             }
    67.         }
    68.     GUILayout.EndHorizontal();
    69. }
    70.  
    71. function PortEntry () {
    72.     GUILayout.BeginHorizontal();
    73.         GUILayout.Label("Port: ");
    74.         var tempPort : int = remotePort;
    75.         var tempPortString : String = remotePort.ToString();
    76.         tempPortString = GUILayout.TextField(tempPortString, 9, GUILayout.Width(200));
    77.         if (tempPortString == "") {
    78.             remotePort = 0;
    79.         }
    80.         else {
    81.             try {
    82.                 remotePort = remotePort.Parse(tempPortString);
    83.             }
    84.             catch (err) {
    85.                 Debug.LogError(err);
    86.                 remotePort = tempPort;
    87.             }          
    88.         }
    89.     GUILayout.EndHorizontal();
    90. }
     
  2. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    Hey,

    though I couldn't test it, I'm sure the root of your problem is a misunderstanding regarding the IP Adresses.

    When the HostData gives you an array of IP-Adresses this is because there can be more than one Adress that points to a certain computer.

    So each entry in an array of IP-Addresses can be a valid IP.

    Since you concatenate all of the IPs when you display them, I guess you expect the user to do the same.
    When you give Network.Connect an array of IPs it checks to see which one it can connect to. When you give it a single string, it tries to connect to that IP. So let the Player enter one IP not all of them.

    Why would you want your player to enter the IP Address manually, when you can handle it automatically?

    It wasn't very pleasing reading your code, btw. Work on that at least when posting something for others to read. It will greatly improve the chance of someone helping you.
     
  3. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    The master server offers NAT Punchthrough which is required for the majority of common users to be able to communicate through UDP as they have not the least bit of an idea of how to setup their routers to autoforward or porttrigger corresponding UDP ports.

    And without that you can try to connect as long as you want, it will never work if the host has not forwarded the port to the "server" (ie the machine on which he is playing)


    Also if you test within the same network or even on the same machine, don't forget that the master server provides multiple IPs. Not only the public (internet IP) but also the LAN IP if it gets it and the local machine IP and when you then connect it tries it against all those (if you used the network sample code as base)
    this is especially important as many routers will not allow you to connect to your own LAN through your internet IP
     
  4. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    That did the trick, thanks. The misunderstanding arose from the fact that HostData.ip returns an array but the docs refer to the return value as the singular "Server IP address" and the output of an individual HostData.ip field appears to be a partial ip addres, so I misconstrued that as meaning each index was a part of one address.

    Can you explain what was unpleasant about the code? I find Unity GUI code onerous at the best of times, so I'm definitely open to ways it can be improved. I realize I did forget to include the relevant class vars, for which I apologize.
     
  5. ProtonOne

    ProtonOne

    Joined:
    Mar 8, 2008
    Posts:
    406
    I think this stems from the strange way some of the official Unity examples are presented. For example the Network.TestConnection code:
    http://unity3d.com/support/documentation/ScriptReference/Network.TestConnection.html

    Combining logic with GUI just confuses things. It took me a long while to suss out what that test was doing. I rewrote it in the following code. I did take out the part that tests a blocked public IP from doing the TestConnectionNAT. My logic is not exactly the same, but I think it is far more clear what is going on. Then I can use the variables I change like "natCapable", "serverMustUseNat", and "canBeServer" when I am drawing my GUI.

    Code (csharp):
    1. function StartTest()
    2. {
    3.     Console.Log("Starting connection test...");
    4.    
    5.     // Force the network test
    6.     natCapable = Network.TestConnection( true );
    7.    
    8.     // Wait for the test to finish
    9.     while( natCapable ==  ConnectionTesterStatus.Undetermined ){
    10.         natCapable = Network.TestConnection();
    11.         yield;
    12.     }
    13.    
    14.     switch( natCapable )
    15.     {
    16.         case ConnectionTesterStatus.Error:
    17.             Console.Log("Problem determining NAT capabilities. Retrying...");
    18.             return;
    19.             break;
    20.        
    21.         case ConnectionTesterStatus.PrivateIPNoNATPunchthrough:
    22.             canBeServer = false;
    23.             break;
    24.        
    25.         case ConnectionTesterStatus.PrivateIPHasNATPunchThrough:
    26.             break;
    27.            
    28.         case ConnectionTesterStatus.PublicIPIsConnectable:
    29.             serverMustUseNat = false;
    30.             break;
    31.            
    32.         case ConnectionTesterStatus.PublicIPPortBlocked:
    33.             break;
    34.            
    35.         case ConnectionTesterStatus.PublicIPNoServerStarted:
    36.             serverMustUseNat = false;
    37.             break;
    38.     }
    39.    
    40.     Console.Log( "Conneciton test result: " + natCapable.ToString() );
    41.     doneTesting = true;
    42. }