Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Port Forward or somethig else ?

Discussion in 'Multiplayer' started by Quantum_Surt, May 24, 2017.

  1. Quantum_Surt

    Quantum_Surt

    Joined:
    Apr 25, 2015
    Posts:
    39
    Hi I am making a really simple game, and 2 players must be able to play with each other.

    So I have a "global server" working on my computer and I port-forwarded the ports for this global server at home.
    So I do not have any problem with this server, any friend is able to connect to it from anywhere because my IP is in the code.

    Then when my server have 2 players or more, it have to tell the first : Create a server, create a client and connect your client to your server with local ip
    and the second : connect to your opponent's server (player 2 have the ip adress of player 1 thanks to global server).

    But the problem is that if player 1 did not port-forwarded for the game, player 2 will never be able to connect to player 1's server...

    That's my issue, I am asking you how can I do so I don't have to ask all players to understand what is port forwarding and to do it right (I'm pretty sure many people are not able to do this, even with help !).

    Thank you
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    A few options that I can think of. Nat traversal (tunnel, maybe? and/or uPnP for routers -- again, people would have to enable this.. afaik, there's no way around this, but it's easier than port forwarding, because it's automatic, after initially allowing it). I do not know how to do either of those, but I know it's possible.

    next option: relay all of the data for the players. You act as "in-between", and forward stuff where it's supposed to go. (obviously this carries a bandwidth cost). You can do this with Unity, built-in, and up to a certain amount is free, as well. I mean, they'd act as the inbetween is what I meant.

    last option I can think of: send instructions for the game .. depending on your audience, that could work.. but you know.. it might suck, too :)

    Hopefully a little bit of that is helpful at least :)
     
  3. Quantum_Surt

    Quantum_Surt

    Joined:
    Apr 25, 2015
    Posts:
    39
    The first option might be the most interesting if players only have to allow it, can you tell me more about it (if you know something ?)
    Thank you !:)
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Not sure exactly about tunneling if you can bypass asking the user.. I think I know what it is, but I don't want to start pretending I'm sure then lead you astray.
    Okay, the next option is uPnP for routers. Like, on my router, I can enable 'Allow uPNP' and if that's on, any program can request permission for a specific (or random port, I guess) to be forwarded to it.. I am 99% sure that's what it does.
    However, as I said before, I do not know how to code a program to actually ask that question to the router :) Never looked into it too deeply.
     
  5. Quantum_Surt

    Quantum_Surt

    Joined:
    Apr 25, 2015
    Posts:
    39
    And how simple enable 'Allow uPNP' is ?
    Ok, I will wait and see if someone have a solution.

    Thank you again for helping me:D
     
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Ya, wait and see if there's more feedback. I'm pretty sure the 'allow' part is very simple, like 1 checkbox in my router setup. heh.
     
  7. MMOInteractiveRep

    MMOInteractiveRep

    Joined:
    Apr 7, 2015
    Posts:
    88
    Instead of waiting or while your waiting why not google it and try to learn on your own how it's done? Your never going to learn for your self if you just have someone give you the answer.
     
  8. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    UPnP is very simple to add.
    This is some sample code that will run it on a separate thread.
    Code (CSharp):
    1. using Open.Nat;
    2. public class UPNP : MonoBehaviour
    3. {
    4.     public bool NATChecked = false;
    5.     public bool NATSuccess = false;
    6.    
    7.     private const string TCPDescription = "<YourGameName> (TCP)";
    8.     private const string UDPDescription = "<YourGameName> (UDP)";
    9.    
    10.     bool _threadRunning;
    11.     Thread _thread;
    12.    
    13.     void OnDisable()
    14.     {
    15.         // If the thread is still running, we should shut it down,
    16.         // otherwise it can prevent the game from exiting correctly.
    17.         if (_threadRunning)
    18.         {
    19.             // This forces the while loop in the ThreadedWork function to abort.
    20.             _threadRunning = false;
    21.  
    22.             // This waits until the thread exits,
    23.             // ensuring any cleanup we do after this is safe.
    24.             _thread.Join();
    25.         }
    26.  
    27.         // Thread is guaranteed no longer running. Do other cleanup tasks.
    28.     }
    29.    
    30.         void ThreadedForward()
    31.     {
    32.         _threadRunning = true;
    33.         bool workDone = false;
    34.  
    35.         // This pattern lets us interrupt the work at a safe point if neeeded.
    36.         while (_threadRunning && !workDone)
    37.         {
    38.             // Do Work...
    39.             AsyncForward().Start();
    40.         }
    41.         _threadRunning = false;
    42.     }
    43.    
    44.     private Task AsyncForward()
    45.     {
    46.         var nat = new NatDiscoverer();
    47.         var cts = new CancellationTokenSource();
    48.         cts.CancelAfter(5000);
    49.  
    50.         NatDevice device = null;
    51.         var sb = new StringBuilder();
    52.         IPAddress ip = null;
    53.  
    54.         return nat.DiscoverDeviceAsync(PortMapper.Upnp, cts)
    55.             .ContinueWith(task =>
    56.             {
    57.                 sb.AppendFormat("[Starting NAT Punchthrough]");
    58.                 device = task.Result;
    59.                 return device.GetExternalIPAsync();
    60.  
    61.             })
    62.             .Unwrap()
    63.             .ContinueWith(task =>
    64.             {
    65.                 ip = task.Result;
    66.                 sb.AppendFormat("\n[Adding TCP Entry To NAT Punchthrough]");
    67.                 return device.CreatePortMapAsync(new Mapping(Protocol.Tcp, NetworkManager.singleton.networkPort, NetworkManager.singleton.networkPort, 0, TCPDescription));
    68.             })
    69.             .Unwrap()
    70.             .ContinueWith(task =>
    71.             {
    72.                 sb.AppendFormat("\n[Adding UDP Entry To NAT Punchthrough]");
    73.                 return device.CreatePortMapAsync(
    74.                     new Mapping(Protocol.Udp, NetworkManager.singleton.networkPort, NetworkManager.singleton.networkPort, 0, UDPDescription));
    75.             })
    76.             .Unwrap()
    77.             .ContinueWith(task =>
    78.             {
    79.                 sb.AppendFormat("\n[Verifying results of NAT Punchthrough]");
    80.                 sb.AppendFormat("\n+------+-------------------------------+--------------------------------+------------------------------------+-------------------------+");
    81.                 sb.AppendFormat("\n| PORT | PUBLIC (Reacheable)           | PRIVATE (Your computer)        | Description                        |                         |");
    82.                 sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
    83.                 sb.AppendFormat("\n|      | IP Address           | Port   | IP Address            | Port   |                                    | Expires                 |");
    84.                 sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
    85.                 return device.GetAllMappingsAsync();
    86.             })
    87.             .Unwrap()
    88.             .ContinueWith(task =>
    89.             {
    90.                 foreach (var mapping in task.Result)
    91.                 {
    92.                     sb.AppendFormat("\n|  {5} | {0,-20} | {1,6} | {2,-21} | {3,6} | {4,-35}|{6,25}|",
    93.                         "[HIDDEN]", mapping.PublicPort, mapping.PrivateIP, mapping.PrivatePort, mapping.Description,
    94.                         mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP", mapping.Expiration.ToLocalTime());
    95.                 }
    96.                 sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
    97.  
    98.                 Mapping[] currentMappings = task.Result.ToArray();
    99.                 bool UDPSuccess = false;
    100.                 bool TCPSuccess = false;
    101.  
    102.  
    103.                 if (currentMappings.Length == 0)
    104.                     NATSuccess = false;
    105.                 else
    106.                 {
    107.                     for (int i = 0; i < currentMappings.Length; i++)
    108.                     {
    109.                         if (currentMappings[i].Description == UDPDescription)
    110.                             UDPSuccess = true;
    111.                         else if (currentMappings[i].Description == TCPDescription)
    112.                             TCPSuccess = true;
    113.                     }
    114.                     if (UDPSuccess && TCPSuccess)
    115.                         NATSuccess = true;
    116.                 }
    117.             })
    118.             .ContinueWith(task =>
    119.             {
    120.                 sb.AppendFormat("\n[Done]");
    121.                 Debug.Log(sb.ToString());
    122.                 NATChecked = true;
    123.             });
    124.     }
    125.  
    126.    
    127. }
    128.  
    This is using the Open.NAT library which got exported with unity compatibility. Can't remember where to find it. The two DLLS you need you can download here:
    https://drive.google.com/file/d/0B8-T4mcelgfROFM4dVYzV3ZTT28/view?usp=sharing


    NAT punchthrough is IMO the easest wait for P2P. And if UPNP fails then you force the player to join someone elses server.
     
    akuno and Quantum_Surt like this.
  9. Quantum_Surt

    Quantum_Surt

    Joined:
    Apr 25, 2015
    Posts:
    39
    Thank you for your answer, I am trying to add your code to my project, but I am in trouble with those 2 lines :
    Code (CSharp):
    1. foreach (var mapping in task.Result)
    2.  
    3. Mapping[] currentMappings = task.Result.ToArray();
    I have this error : Error CS1061: 'System.Threading.Tasks.Task' does not contain a definition for 'Result' and no extension method 'Result' accepting a first argument of type 'System.Threading.Tasks.Task' could be found (are you missing a using directive or an assembly reference?) (CS1061) (Assembly-CSharp)

    And I don't really find a way to fix it !

    Thank you again for your help !
     
  10. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    uhm. Did you include both DLL's i provided?
     
  11. Quantum_Surt

    Quantum_Surt

    Joined:
    Apr 25, 2015
    Posts:
    39
    Yes, I did, rest of the code is ok...
     
  12. Quantum_Surt

    Quantum_Surt

    Joined:
    Apr 25, 2015
    Posts:
    39
    My complete code :
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Text;
    5.  
    6.  
    7. using System.Net;
    8. using UnityEngine.Networking;
    9. //using UnityEditor.VersionControl;
    10. using System;
    11. //using System.Collections;
    12. using System.Threading;
    13. using System.Diagnostics;
    14. using System.Threading.Tasks;
    15.  
    16. using Open.Nat;
    17.  
    18. public class UPNP : MonoBehaviour
    19. {
    20.     public bool NATChecked = false;
    21.     public bool NATSuccess = false;
    22.  
    23.     private const string TCPDescription = "<YourGameName> (TCP)";
    24.     private const string UDPDescription = "<YourGameName> (UDP)";
    25.  
    26.     bool _threadRunning;
    27.     Thread _thread;
    28.  
    29.     void OnDisable()
    30.     {
    31.         // If the thread is still running, we should shut it down,
    32.         // otherwise it can prevent the game from exiting correctly.
    33.         if (_threadRunning)
    34.         {
    35.             // This forces the while loop in the ThreadedWork function to abort.
    36.             _threadRunning = false;
    37.  
    38.             // This waits until the thread exits,
    39.             // ensuring any cleanup we do after this is safe.
    40.             _thread.Join();
    41.         }
    42.  
    43.         // Thread is guaranteed no longer running. Do other cleanup tasks.
    44.     }
    45.  
    46.     void ThreadedForward()
    47.     {
    48.         _threadRunning = true;
    49.         bool workDone = false;
    50.  
    51.         // This pattern lets us interrupt the work at a safe point if neeeded.
    52.         while (_threadRunning && !workDone)
    53.         {
    54.             // Do Work...
    55.  
    56.             AsyncForward().Start();
    57.         }
    58.         _threadRunning = false;
    59.     }
    60.  
    61.     private Task AsyncForward()
    62.     {
    63.         var nat = new NatDiscoverer();
    64.         var cts = new CancellationTokenSource();
    65.         cts.CancelAfter(5000);
    66.  
    67.         NatDevice device = null;
    68.         var sb = new StringBuilder();
    69.         IPAddress ip = null;
    70.  
    71.         return nat.DiscoverDeviceAsync(PortMapper.Upnp, cts)
    72.             .ContinueWith(task =>
    73.                 {
    74.                     sb.AppendFormat("[Starting NAT Punchthrough]");
    75.                     device = task.Result;
    76.                     return device.GetExternalIPAsync();
    77.  
    78.                 })
    79.             .Unwrap()
    80.             .ContinueWith(task =>
    81.                 {
    82.                     ip = task.Result;
    83.                     sb.AppendFormat("\n[Adding TCP Entry To NAT Punchthrough]");
    84.                     return device.CreatePortMapAsync(new Mapping(Protocol.Tcp, NetworkManager.singleton.networkPort, NetworkManager.singleton.networkPort, 0, TCPDescription));
    85.                 })
    86.             .Unwrap()
    87.             .ContinueWith(task =>
    88.                 {
    89.                     sb.AppendFormat("\n[Adding UDP Entry To NAT Punchthrough]");
    90.                     return device.CreatePortMapAsync(
    91.                         new Mapping(Protocol.Udp, NetworkManager.singleton.networkPort, NetworkManager.singleton.networkPort, 0, UDPDescription));
    92.                 })
    93.             .Unwrap()
    94.             .ContinueWith(task =>
    95.                 {
    96.                     sb.AppendFormat("\n[Verifying results of NAT Punchthrough]");
    97.                     sb.AppendFormat("\n+------+-------------------------------+--------------------------------+------------------------------------+-------------------------+");
    98.                     sb.AppendFormat("\n| PORT | PUBLIC (Reacheable)           | PRIVATE (Your computer)        | Description                        |                         |");
    99.                     sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
    100.                     sb.AppendFormat("\n|      | IP Address           | Port   | IP Address            | Port   |                                    | Expires                 |");
    101.                     sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
    102.                     return device.GetAllMappingsAsync();
    103.                 })
    104.             .Unwrap()
    105.             .ContinueWith(task =>
    106.                 {
    107.                     foreach (var mapping in task.Result)
    108.                     {
    109.                         sb.AppendFormat("\n|  {5} | {0,-20} | {1,6} | {2,-21} | {3,6} | {4,-35}|{6,25}|",
    110.                             "[HIDDEN]", mapping.PublicPort, mapping.PrivateIP, mapping.PrivatePort, mapping.Description,
    111.                             mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP", mapping.Expiration.ToLocalTime());
    112.                     }
    113.                     sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
    114.  
    115.                     Mapping[] currentMappings = task.Result.ToArray();
    116.                     bool UDPSuccess = false;
    117.                     bool TCPSuccess = false;
    118.  
    119.  
    120.                     if (currentMappings.Length == 0)
    121.                         NATSuccess = false;
    122.                     else
    123.                     {
    124.                         for (int i = 0; i < currentMappings.Length; i++)
    125.                         {
    126.                             if (currentMappings[i].Description == UDPDescription)
    127.                                 UDPSuccess = true;
    128.                             else if (currentMappings[i].Description == TCPDescription)
    129.                                 TCPSuccess = true;
    130.                         }
    131.                         if (UDPSuccess && TCPSuccess)
    132.                             NATSuccess = true;
    133.                     }
    134.                 })
    135.             .ContinueWith(task =>
    136.                 {
    137.                     sb.AppendFormat("\n[Done]");
    138.                     Debug.Log(sb.ToString());
    139.                     NATChecked = true;
    140.                 });
    141.     }
    142. }
    143.  
     
    akuno likes this.
  13. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,168
    Gonna be honest. No clue at the moment. And I don't have too much time to look into it at the moment. My game is going into Alpha shortly. But for now I guess try to cut those lines. That will however mean you don't get any status of if it succeeded or not. If you REALLY need that i'll try to recreate this and fix it.
     
  14. thegreatzebadiah

    thegreatzebadiah

    Joined:
    Nov 22, 2012
    Posts:
    836
    @HellSon It sounds like what you're looking for is my asset, NAT Traversal, which will automatically perform punchthrough and port forwarding for you. Plus some other cool things optionally falling back to the relays when all else fails.
     
  15. gerardB_fr

    gerardB_fr

    Joined:
    Jul 21, 2016
    Posts:
    28
    Hellson,

    It's a linq extension , add:

    using System.Linq;


    Greetings.
     
    akuno likes this.