Author Topic: How to properly connect to a remote Lobby server?  (Read 9763 times)

Yoi

  • Newbie
  • *
  • Thank You
  • -Given: 4
  • -Receive: 0
  • Posts: 8
    • View Profile
How to properly connect to a remote Lobby server?
« on: March 31, 2017, 11:36:42 AM »
Hi everyone!

First off I want to say I'm very much a newbie to TNet and Multiplayer/Networking-development in general.

That said, I only hear great things about TNET, but as many newbies here have obviously noted in the past, documentation is somewhat lacking.
Don't get me wrong, there are a few written tutorials, an (apparently slightly outdated?) video tutorial and fairly extensive documentation for every function in the source code.
And as many have stated in the past, Aren is doing a great job of handling the forums with brief but to the point input and answers.
There does however seem to be a ton of people having a hard time getting over that first threshold (myself included), setting up a proper connection to a standalone (.exe) remote lobby server and getting things up and running with instantiating and connecting to a server + reporting instances to the remote lobby.

Even after spending two full days reading just about every forum post on the subject, going through the entire source code script by script and looking at every sticky tutorial, I still can't make heads or tails of it, and humbly ask those of you who "get it" to drop some knowledge in this post.

So, first off, what I'm trying to accomplish is the following:
- Launch a copy of the standalone TNServer.exe registered only as a lobby server.
- Inside Unity: Launch a game server (TNServerInstance.Start()), thus creating a server (or room) for other players to connect to.
- Report the new game server instance to the lobby server (with name, num of connected players and a set player limit of say 4 players), making the game server/room visible to other players as a list (to which connectivity buttons could be added)
- Connect one or more clients to the instantiated game server (and updating status in the lobby server).


The idea is of course to have a lobby server, listing many instances of game servers - and allowing other players/clients to connect to one of these listed game servers.

Let's start with the TNServer.exe (Lobby Server):
This needs to be set up in a publicly available space (say Amazon EC2 or simply a home computer exposed on the internet; firewalls open/ports forwarded in router, etc).
In my case, I've set it up on my home server (available within the LAN as 192.168.10.150). But it will of course be using the external IP (or connected domain name) things get off the ground. TNServer.exe is currently launched with the following args: TNServer.exe -name "LOBBY" -tcpLobby 5129
The reason I've used TCP instead of UDP is to avoid burst of keep alive transmissions and allowing for use of for example Amazon EC2, which apparently limit UDP traffic?)
The lobby server launches as expected (listing external IP, TCP Lobby Server started on port 5129, and so on). For some reason UPnP doesn't work for me even though my router supports it and it's enabled, but from what I gather, that doesn't matter any way. I've also portforwarded ports 5127-5129 for the servers external IP through the router.

Let's proceed to Unity and instantiating/creating a game server:
It seems the recommended way to create a game server and connecting it to a remote lobby is the following method:
  1. static bool TNet.TNServerInstance.Start (int tcpPort, int udpPort, string fileName, Type type, IPEndPoint remoteLobby, bool openPort = true)           
  2. // Start a local game server and connect to a remote lobby server.
(See: http://www.tasharen.com/tnet/docs/class_t_net_1_1_t_n_server_instance.html#a2fa49c255504f3be28ca60c2f3ab880e for more info)

This could translate to a script looking something like this, right?

  1. void Start() {
  2.     if (!TNServerInstance.isActive) {
  3.         Debug.Log("Starting server");
  4.         TNServerInstance.serverName = "Test Game Server";
  5.         TNServerInstance.Start(5127, 5128, null, TNServerInstance.Type.Tcp, Tools.ResolveEndPoint("192.168.10.150", 5129));            
  6.     } else {
  7.         Debug.Log("Server already started");
  8.     }
  9.     TNManager.Connect();
  10. }

This seems to create a Game Server called Test Game Server with one connected player (judging from the _Server game object in the hierarchy (under DontDestroyOnLoad, at runtime). From what I understand the Lobby Server should give some kind of response whenever a game server is registered, but since I'm not getting any response what so ever from the Lobby server upon running the script above, I'm assuming I'm just instantiating a game server and connecting to it - and preparing to register it with the lobby server...? I'm assuming something is missing that actually registers the instance with the lobby server? Does this make sense? Am I missing something here?

If we proceed to the part about getting a list from the Lobby Server:
There's a reference to the TNTcpLobbyClient-script, in "Example 0 - Menu.pdf" (included in the examples/documentation in the Unity Asset Bundle):
Quote
A Server Lobby allows for a server list to be displayed to the user and in this example it will show up as a list of IP addresses to the right of the Connect and Start Server buttons. This example uses a UDP based lobby, which is good for searching the Local Area Network for fellow gamers, but in other types of games, such as Windward, you would want to use a TCP based lobby on a central server. TNet comes with the script TNTcpLobbyClient script to do this.

I have no idea how to use the TNTcpLobbyClient-script properly, but I've tried using some snippets of example code for listing registered servers, included in the PDF:

  1. // List of discovered servers
  2. List<ServerList.Entry> list = TNLobbyClient.knownServers.list;
  3.  
  4. // Server list example script automatically collects servers that have recently announced themselves
  5. for (int i = 0; i < list.size; ++i) {
  6.     ServerList.Entry ent = list[i];        
  7. }
  8.  
  9. Debug.Log(list.size);

List.size returns 0, so again, I don't think anything has been registered with the lobby server. And this is about where I'm currently stuck.

So if you know what's missing or what I'm doing wrong, please help me fill in the blanks and get things up and running.

Thank you all - and have a great weekend!


ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #1 on: March 31, 2017, 01:52:47 PM »
TcpLobbyClient script can be attached to a game object. You can specify the remote address on it, and it will automatically connect and populate the list of known servers for you to use. If you grab the free Starlink UI Kit from the Asset Store, there is a TNet version of it that uses the remote TCP-based lobby. In short, it's basically a fire-and-forget type of script. If you have it in the scene, it will work.

The ExampleMenu script that comes with TNet shows how TNLobbyClient.knownServers.list is used -- line 373 of that script. The list is populated by a lobby client script -- such as TcpLobbyClient explained above.

You don't need to specify the UDP port (5128) when calling TNServerInstance.Start if you don't need UDP. Just pass '0'.

You will indeed get a notification of a server being added on the lobby server side. Did you try connecting to http://192.168.10.150:5129 via a web browser? You will get "no data received" if it's accessible, or more info if you ran the lobby with the -http flag and try to connect to the game server port. In general I'd recommend running the lobby somewhere external, not local -- and even if you do run it local, you should be using your external address instead of your LAN address.

For Starlink, I launch it with the following parameters which sets up the lobby + game server at the same time:
  1. TNStarlink.exe -name "Amazon" -tcp 5135 -tcpLobby XXX.XXX.XXX.XXX 5134 -localPath -http -app Starlink
..where XXX.XXX.XXX.XXX is the address of the EC2 server instance where the TNServer was launched.

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #2 on: March 31, 2017, 03:05:11 PM »
It appears the TcpLobbyServer has a minor bug in TNet3 in that it never outputs when a server is registered.
Fix:
In TNTcpLobbyServer.cs near line 312, find:
  1. if (old != null)
  2. {
  3.         if (old.playerCount == ent.playerCount && old.name == ent.name)
  4.                 noChange = true;
  5. }
  6.  
  7. if (!noChange) mLastChange = mTime;
  8.  
  9. #if STANDALONE
  10.         if (tcp.Get<long>("lastSend") != 0)
  11.                 Tools.Print("[+] " + ent.name + " (" + ent.playerCount + ")");
  12. #endif
  13.  
Replace with:
  1. if (old != null)
  2. {
  3.         if (old.playerCount == ent.playerCount && old.name == ent.name)
  4.                 noChange = true;
  5. }
  6. #if STANDALONE
  7. else
  8. {
  9.         Tools.Print("[+] " + ent.name + " (" + ent.playerCount + ")");
  10. }
  11. #endif
  12.  
  13. if (!noChange) mLastChange = mTime;
  14.  
For ArenMook: the bug is that a lobby link never has its "lastSend" value set because it's skipped in the relevant iteration. In fact, lastSend being 0 is what's used to detect if a connection is a lobby link or not :P



I basically copy-pasted your example code for starting the server instance and everything worked as expected.
  1. public string RemoteLobbyServerIPEP = "192.168.1.2:25565";
  2.  
  3. public void btnStart_OnClick()
  4. {
  5.         if (!TNServerInstance.isActive)
  6.         {
  7.                 TNServerInstance.serverName = "TestGameServer";
  8.                 if (TNServerInstance.Start(5127, 5128, null, TNServerInstance.Type.Tcp, Tools.ResolveEndPoint(RemoteLobbyServerIPEP)))
  9.                         Debug.Log("Local server started");
  10.                 else
  11.                         Debug.LogError("Unable to start local server");
  12.         }
  13.         TNManager.Connect();
  14. }
  15.  

UPnP reports failure in both Unity and the stand-alone lobby server.
I don't have a second machine on my LAN to test with so this is all on my local PC. I think Windows autoresolves my LAN address to loopback as Wireshark isn't picking up any packets to the lobby server. However, so long as you're testing with your LAN address and not your public address, I don't see how this difference would effect you. Make sure you create a firewall rule for TNServer.exe at the OS level too.

For a server list, just place the TNTcpLobbyClient component on any gameobject in the scene. Set the remote address and remote port to your lobby server. Your iteration code looks fine. I subscribe to the TNTcpLobbyClient.onChange event and repopulate my serverlist there. Makes more sense with Unity's new GUI system.

You'll probably want to subscribe to TNManager.onConnect and TNManager.onDisconnect. These will be fired whenever something happens related to the server connection. In onConnect, if the connection succeeds you'll then want to call TNManager.JoinChannel specifying a channelID, scene to load, etc etc.

Yoi

  • Newbie
  • *
  • Thank You
  • -Given: 4
  • -Receive: 0
  • Posts: 8
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #3 on: April 03, 2017, 05:17:05 AM »
Thank you both for your amazing input!

I just ran a few tests and everything works perfectly - but I had to recompile a new TNServer.exe with the suggested changes in the TNTcpLobbyServer-script.
Without the changes, TNServer.exe (included in TNET 3.0.8 ) didn't register the lobby and therefore there was no response. Also tested by adding the -http flag to the launch parameters for the .exe and could confirm 0 servers and 0 players when connected before the changes - and "Servers: 1, Players: 1, 1 TestGameServer" after adding the change to TNServer.exe project and recompiling. Would never have figured that out on my own, so thank you!

Upon connecting from two different clients, I'm now getting:
Quote
[TimeStamp] [ + ] Test Server 1 (0)
[TimeStamp] [ + ] Test Server 2 (0)

Does the (0) at the end represent number of connected player? Cause there should be one connected player (TNManager.Connect() is present and _Server reports PlayerCount=1)
I'm guessing it's because there are 0 players connected when the server is instantiated and TNServer.exe outputs the data, as connection happens after the instantiation of the server.
Player data is clearly stored properly, as it's accessable via the http-interface (and probably via the in-game server list, once implemented). Is there ever a case when it reports other than (0)?

NOTE! There also seems to be something missing in the RemoveServer function of TNTcpLobbyServer causing the removal/disconnect of servers to not output...
Maybe something to add in the next official build, Aren?

Last quick question regarding the TNServer.exe args for Starlink: what does the -localPath arg do? Can't seem find anything about it in the documentation or source code.

Again, thank you both!
We have a working lobby-server!

Yoi

  • Newbie
  • *
  • Thank You
  • -Given: 4
  • -Receive: 0
  • Posts: 8
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #4 on: April 03, 2017, 11:05:11 AM »
OK, so everything currently works great - but only as long as the hosting and connecting machine is on the same network/LAN...
But whenever I try to connect from a computer outside the network, I get the server-listings and communication with the lobby server works great, but I can't connect to the instanced game server. Could it be a NAT-issue or what are the main reasons?

What we have now is the following setup:
Lobby-server: behind a router, but with portforwards in place, accessable via an external IP and domain, for instance lobby.mydomain.com:5129. No problem so far.
Client 1: Sits behind a router on a home broadband, no port forwards but windows firewall is allowed for public connections (or completely disabled if you will), starts an instance of a game server (hosts a game).
Client 2: Sits on different broadband connection (different external IP), no portforwards (windows firewall completely disabled) sees the instance server in the list thanks to the lobby server, but connecting to it fails (are there error codes / debug info available somehow?).
The game shows up in the server-listings on both client 1 and 2, but I can't get the connection between the two - unless they're on the same network (making the essentially connect via LAN IP).

I've set the port-numbers to randomize 10000-40000 for both TCP and UDP when instancing the server (to allow for multiple instances from within the same LAN).
Shouldn't matter to the connecting client, as the portnumber is send along with the externalAddress (IP) to the lobby server.

The Connect-code attached to the button (entirely copy-pasted from DrawServerList-function in ExampleMenu.cs) is:
  1. TNManager.Connect(ent.externalAddress, ent.internalAddress);
I've also tried (without success):
  1. TNManager.Connect(ent.externalAddress, ent.externalAddress);

As I understand it, there no support for NAT Punchthrough in TNET3 (partially as this is a UDP-feature and TNET is mostly TCP)...
Is there anything else I need to do in order to facilitate the connect-process across different internet connections so users don't have set up portforwards/stuff in order to host a game?

I'm attaching my source code, just in case.
I also have the standard TNTcpLobbyClient attached to a gameobject targeting the lobby-server (externalIP/domain, port 5129).

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #5 on: April 03, 2017, 02:33:58 PM »
I'd set up Wireshark and see if the packets are actually arriving on Client 1.

There should be errors, yes:
Subscribe to TNManager.onError and TNManager.onConnect, both provide error messages.

As for the lobby server, yes it seems the same bug exists in the RemoveServer() function in TNTcpLobbyServer.cs. Additionally, the (x) value should be the player count, yes. No idea what's happening there; potentially a bug in the lobby link class? The lobby server code is and always has been so janky, I feel like it was written in haste. Could probably use a revisit from ArenMook.

Yoi

  • Newbie
  • *
  • Thank You
  • -Given: 4
  • -Receive: 0
  • Posts: 8
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #6 on: April 04, 2017, 05:00:54 AM »
So for some reason, everything works if I do:
  1. TNManager.Connect(ent.externalAddress.ToString());
instead of
  1. TNManager.Connect(ent.externalAddress, ent.internalAddress);

+ There was also a bug in the windows firewall on one of my machines, where connection was actively refused in one direction, even though it was turned off.
This was resolved by reverting Windows Firewall to their default settings and then simply allowing for local and remote connections in the popup when hosting the game server.

Both local and external (different external connection / across internet) connections can now connect as expected.
Unless I disable UPnP the router of the hosting machine (or the hosting computer is on a 3G/4G/LTE-connection or a router without UPnP).
Then the user has to know how to open ports in their router, etc - which unfortunately is an absolute dealbreaker for our project.
Which leaves us with two options for user-friendly connections: TCP Punchthrough or dedicated hosting of game servers (which for us is out of the question at this point).

From reading several posts, I understand NAT Punchthrough to be UDP only (Aren refers to it as a UDP hack not supported by TNET in pretty much every post asking about it + that TNET is mostly TCP-based which wouldn't work with NAT Punchthrough for UDP). Cmifwdll, you mentioned an alternative to UDP-based NAT Punchthrough: TCP Punchthrough/Holepunching (in an older post) - do you know if anyone has implemented a 3rd party solution for this which integrates with TNET? Do you think it would even be possible - for a far more experienced network programmer than me, that is?

Any other ideas?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #7 on: April 04, 2017, 05:23:05 AM »
As mentioned in the other post, NAT punchthrough is for IPv4. It's my understanding that IPv6 avoids that whole mess. When publishing for Apple you can't even submit IPv4-only apps -- haven't been able to for ~2 years now. They're forcing app apps to have IPv6 support.

Hosting servers on a 3G/4G/LTE connection isn't going to be possible. Carrier networks explicitly prohibit this.

Ideal hosting is done on a public-accessible server. For Windward I used an Amazon EC2 instance where I have both the lobby server and the game server hosted. You can start with their free EC2 instance (free for a year) and upgrade it when you need it -- or pick any other VPN hosting service. Most games can be done using a single server -- players connect, then join different channels, with each channel being a separate game instance. One server, many concurrent games.

Starlink UI kit (available for free on the Asset Store) is an example of a game I made where I had one central server, yet players could create any number of concurrent games on that server.

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: How to properly connect to a remote Lobby server?
« Reply #8 on: April 04, 2017, 08:29:11 AM »
Have to agree with what ArenMook said. You can rent a VPS for $5 a month and it'll most likely have enough resources to handle TNet (based on ArenMook's Windward stats).

TCP holepunching is going to be more effort than it's worth.