How to create "Lobby System" for Android games?

Godot Version

Godot Engine 4.3

Question

I am trying to create a lobby system for my android game on godot engine.

What I mean by lobby system is that…
there are plenty of mobile games for android that have multiplayer (and that are made on unity). Basically, let’s say you’re a player. You as a player from main menu press “create new lobby”, after that new lobby was created. You can play, walk around and do whatever you want. But, this lobby has appeared in the global lobby list.
Other players in the global internet can see your created lobby in the global lobby list and can join your lobby.

I have two questions: how to create it? and is my implementation idea would be technically good for that?

My implementation idea:

I have dedicated server on linux. I can export my godot engine project as both “Android” instance and as “Dedicated Server” instance. For “Dedicated Server” specific things I can always check OS.has_feature("dedicated_server").
Now the thing is that my actual linux dedicated server is django web application.
Basically, through gunicorn and nginx all requests to my domain are forwarded to my django web application.

So instead of having a headache in forwarding some requests to my dedicated server godot engine I decided to use my django web application. There will be certain API urls registered specifically for my game like so: domain.com/create_lobby, domain.com/join_lobby. (The only problem is that for POST requests django requires csrf-tokens in terms of security, so I would need to come up with some kind of API token system or move to the REST framework addon of django.)

The create lobby is the neat part.
As far as I am aware… Godot Engine 4 high-level multiplayer API primarily uses peer-to-peer connections. Because of that I decided to:

  1. HTTPRequest to “create_lobby” returns port for that new created lobby.
    * What I mean by port? There will be specific range of ports (let’s say 7000-7100) specifically for lobbies. Everytime a new lobby is created then free port will be returned and dedicated for client players to connect to new created lobby on my server through peer-to-peer connection.
  2. All lobbies are saved in django database… perhaps would be cool feature in the future to create not just simple lobbies but special “community” lobbies that have custom settings on them that are setup by player lobby owner.

Now you probably see what I mean. Because high-level multiplayer API primarily uses peer-to-peer connections, everyime new lobby is created → specific port is being dedicated for that lobby in order to establish peer-to-peer connection between client and server → new godot engine “dedicated server” subprocess is created that has this port used for basically server.

During creation of new lobby, there will be launched new “subprocess” of my godot engine “Dedicated Server” exported game with specific port being set as launch argument (OS.get_cmdline_args() like this for launch arguments).

In this dedicated server subprocess there will be all things that server does: such as synchronizing player movements, actions and etc.

The only thing that makes me worry about this implementation is the port dedication part. Do any of these types of games that has such system actually do that? Perhaps there is other way around? I haven’t checked unity templates for this if there is really are though… perhaps I should?

I am NOT gonna use external services such as W4.

Bump. I am still in the process of making “Lobby System” and I kind of want to know if this implementation idea is actually good in terms of production implementation and security. Is this even good idea to make it like that, perhaps other games that use such system actually use different approach for this?

All I can say is try it and see what happens. Multiplayer is getting better for Godot, but there’s still a lot of grunt work to do. I do not know if the hoops you are jumping through for the port allocation are necessary. If you have a dedicated server, that should be hopefully handled by the engine. If not, well you’ll know once testing it.

I’m curious what the purpose of connecting to a webserver at all is for you. Is this somehow for matchmaking? Or a leaderboard?

Hello! Thank you for answering.

I would use webserver primarily for two lobby types. Basically, players would be able to create “Community” lobby. “Community” lobby allows the owner of that lobby to change specific lobby settings such as related to matchmaking, round system, what map will be, what roles will be enabled and etc.

Well atleast it’s one of my ideas from using a webserver. Actually, I am connecting all of that to webserver because all requests going to my domain.com through nginx and gunicorn are forwarded to my django web application. That would be quite painful for me to setup it differently and somehow differentiate when requests are needed for godot engine and when for django web application.

For “leaderboard” I will perhaps use Google Play services.

To be honest I am not sure about port allocation either. I wanted to make “lobby” system where there are multiple lobbies. As far as I am aware… through high-level multiplayer API I could just use one opened port with one ip-address to establish peer-to-peer connection between client and server. But this would mean that only one lobby would persist in the game at all instead of multiple lobbies in the game that have their own settings and everything their own things happening on them.

1 Like

You are trying to shoehorn a lot into your current django setup it sounds like. I honestly don’t think you need to do that much work. Just forward everything through one port on the way in, and then see what happens.

Well but isn’t it’s a peer-to-peer connection? 1 ip + 1 opened port = 1 server.

In “Lobby” systems you can create plenty of lobbies that are basically their own servers (they have their own things happening on them such as their own players, player movements, player actions, settings, rounds and etc.)

How then players will be able to create multiple lobbies that are basically their own servers? Peer-to-peer connection would be established only at one ip address at one port. My dedicated server godot engine game would listen to that port to connect players and that would be just one lobby.

Well I guess I will try that just for testing purposes.

No.

So let’s take a look at a web server for a minute. For the purpose of this discussion, we are going to say that you are running your web server from your home. It’s a single box, and you have your personal webpage on it. All web servers run on port 80. All secure web server run on port 443. So you’ve secured your web server, and run everything over port 443. However, your web server still listens on port 80. (More on that in a moment.)

So now I visit your web address. I type in http://eliaslucky.com. My request gets sent to a DHCP server which gives my browser the IP address. Let’s say it’s 247.183.6.29 (I made that up) My web browser takes that IP address and appends the port to it. Now I’ve typed in http so my browser assumes an insecure connection and uses port 80.

My browser requests a connection, and data, from 247.183.6.29:80. Your web server is sitting at that address because that’s the public IP address your Internet Service Provider (ISP) gave you. That goes to your local network, passing through your router, and being redirected to your webserver because you have port forwarded port 80 and 443 to your web server. Your webserver says, oh hey, yes here’s my home page html. But also, I would like to run securely using https. Is that ok?

Your response comes back over the internet, and my browser says, “Yes I support https.” It subtly changes the address in my browser bar to https://eliaslucky.com and then send the next request from me (when I click a link) to 247.183.6.29:443. As long as we continue talking, all traffic is going over port 443.

Now, I am one of hundreds of users on your web page at the moment (go you!) Every person is sending and receiving data from one port. Either port 80 or port 443. So over time, everyone is using port 443. Each request that goes in, goes to 247.183.6.29:443 and every request that goes out goes to a separate IP address - the public IP of each person browsing your website.

For context, the first lobby system I worked on was back in '99 and it was ultimately used for a number of Electronic Arts games in the 2000’s. I mention this so you understand that I know what I am talking about.

Regardless of the number of lobbies you have, you have a central server serving them. Individual players are not running lobbies on their own boxes. If they are, then you are not running a client-server architecture. You are running a peer-to-peer architecture.

I want to clarify something you said in your previous post. The above statement is an oxymoron. There is no such thing as a peer-to-peer connection between a client and server. You either have a client-server connection or a peer-to-peer connection.

In client-server architecture, the server is the authority on all data. The client says “I want to do this,” and the server says “Here’s what happened.” In a peer-to-peer connection, Peer 1 says “I did this and this is what happened,” and Peer 2 says, “Ok, and I did this and this is what happened.”

Another way to look at it is Client-Server is playing D&D with a DM. Peer-to-peer is playing D&D with your friends without a DM and using improve “yes, and…” rules where you have to accept everything your friend says as true and play off it. No one is in charge.

So if you want players to create their own servers, you would have them hit a Host button. That would send their connection info to your server, and then you could send that out to anyone playing the game and querying for servers through an API. Then they could establish a connection to that server.

Keep in mind that means the hosting player can hack the game and there’s nothing the other players can do about it.

The port is pretty immaterial. But multiple IP addresses can come into one port. The real life equivalent of what you’re describing would be to say that you need a separate email address (port) for every different person who send you email, because otherwise you wouldn’t know who sent you the email.

This is where I’m confused. Who is running the servers? You or the players? Because you keep talking peer-to-peer, but then you mention a dedicated server.

Again, if we go back to the web page example, the assumption you seem to be making is that if someone were to go from your home page to your blog, they would need to be on a different port or you wouldn’t know what page they are on. You can have multiple lobbies running on the same port. You just have to know what lobby they are connected to in their connection string.

Conclusion

You have barely scratched the surface of this. To do peer-to-peer networking, you need to understand how to do NAT punch-throughs which is a complicated subject. You also need to think about networked physics.

Also, make sure you fully understand the terms you are using so that you get the correct answers to your questions.

Wow, thank you so much for such good response. In all technical things you’re indeed right.

What I meant by saying peer-to-peer is… perhaps my bad understanding of networking in the “Godot Engine multiplayer high-level API” part. Specifically, I use ENetMultiplayerPeer for my networking needs. Which the official Godot Engine documentation states about is this:

A MultiplayerPeer implementation that should be passed to MultiplayerAPI.multiplayer_peer after being initialized as either a client, server, or mesh. Events can then be handled by connecting to MultiplayerAPI signals. See ENetConnection for more information on the ENet library wrapper.

In terminology, I think, I perhaps got confused because the documentation states that this is MultiplayerPeer implementation. And from multiple tutorials I’ve watched and read the ENet MultiplayerPeer can actually be used as peer-to-peer (when client is hosting a game and another client joins his game) and as part of Dedicated Server (client joins a server in which server hosts a game while listening to specific port and client joins using that specific port).

What I mean by allocating each new port for each new lobby is because the way the implementation of ENetMultiplayerPeer work.
Specifically, in order to “host a server” I would need to do this:

func create_server_peer(port : int, max_players : int = 30):
	var enet_network_peer : ENetMultiplayerPeer = ENetMultiplayerPeer.new();
	enet_network_peer.create_server(port, max_players);
	multiplayer.multiplayer_peer = enet_network_peer;

Now as you can see, in order to use create_server method I am required to use specific opened port. This would mean if I want to create multiple “lobbies” then according to this implementation I would need multiple ports (each new port assigned to each new lobby).

For a player (or a client) to connect to hosted game, an ip address with open port is required. Like this:

func create_client_peer(port : int):
	var enet_network_peer : ENetMultiplayerPeer = ENetMultiplayerPeer.new();
	enet_network_peer.create_client(SERVER_IP, port);
	multiplayer.multiplayer_peer = enet_network_peer;

Which is very specific and interesting. It seems like in order to join… let’s say Lobby B in a same server ip address I would require different opened port, so I would not join Lobby A that is also hosted in that server ip address with specific port?


My idea was to use my same game project (client version) but there would be check at the _ready function to determine if the game that is being executed is actually a Dedicated Server
(In other words I check like this if OS.has_feature(“dedicated_server”) if it’s Dedicated Server game instance; so I would have same project but I would be able to export it for both as Client Game instance and as Dedicated Server instance).

If OS.has_feature(“dedicated_server”) returned true then I would do server specific things.
But there is pretty neat part:
I would then retreive command line arguments using OS.get_cmdline_args(). The command line arguments would be --port. And then using that above implementation of ENetMultiplayerPeer I would host a server using that specified port.

Now why I would read command line arguments? This Dedicated Server game instance would be started as a process from my django web application.

The way the player would create a new lobby is by clicking a GUI button. When clicked, the script would send web request using HTTPRequest node to my specific API call (e.g. domain.com/create_lobby). My django web application receives this web request and then does this:

  1. Saves the newly created lobby in the SQL databse with specified lobby name, lobby max amount of players, lobby description (if it’s a “Community” lobby type), lobby_type (enum - either “Default” or “Community”) and etc.
  2. Finds free unused port. Basically, checks if randomy selected port in the range of 7000-7100 is either used in the SQL database by other lobby or is not used. If port is unused by other lobbies then it’s saved for the future.
  3. Starts my Godot Engine game process (and because there are OS.has_feature if-statements the game works as Dedicated Server) with specified command line arguments in which there is --port argument (example: /path/to/game/lobbysystem --port 7550 --lobby-id 5; lobby-id in this part is required for some logical reasons in the server specific scripts).
  4. Django web application finally responds to the web request in JSON format. In this JSON response there will be allocated port. The player’s game receives the response and joins the hosted server through ENetMultiplayerPeer using the create_client_peer method I’ve presented above.

I hope I’ve described what I mean clearly. Perhaps, I am missing something and actually there is different approach to “Lobby System” without using ENetMultiplayerPeer… or there is actually different methods in that class that allows to do what I want in a different way.

But from my understanding of how multiplayer high-level API in Godot Engine works, this is the actual way to do what I want.


My notes: According to ENetMultiplayerPeer to host a game I need opened port. Which means that I would not be able to host multiple lobbies in the same game instance that is being executed in real time. Atleast that’s how I understand from the official Godot Engine documentation:

Create server that listens to connections via port. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use set_bind_ip().

To join a game I need an ip address and opened port. Which means that there is should be some kind of game isntanced launched on a specific ip address that listens to that specified port. Atleast that’s how I understand from the official Godot Engine documentation:

Create client that connects to a server at address using specified port. The given address needs to be either a fully qualified domain name (e.g “www.example.com”) or an IP address in IPv4 or IPv6 format (e.g. “192.168.1.1”). The port is the port the server is listening on.

I don’t know if I can host multiple games (lobbies) in one launched game instance using ENetMultiplayerPeer… atleast I haven’t found it…
and also most tutorials don’t do that so I haven’t even noticed if it’s even possible.

I suppose the Godot Engine kind of mixes the peer-to-peer connection terminology because of the way the multiplayer API is implemeneted.

Let’s say:
Player A can host a game in his game instance. The key thing is that the game is being hosted using that ENetMultiplayerPeer method. But at the same time the game also spawns the player’s character so he would play too while he is hosting.

Another Player B can… join him in whatever possible way either LAN (using local ip address like this 192.168.1.1), NAT or global internet. No matter what way the opened port is required (in simple terms, if we don’t go into specifications that for global internet you need both opened port in the firewall and on the router; and that for LAN you can just have opened port in the firewall and etc.).

In my situation with “Dedicated Server” which runs on linux I basically do same thing but instead the player’s character is not spawned. So it’s kind of like peer-to-peer connection but it’s actually was turned into client to server connection because the server doesn’t play the game (his character is not being spawned in the hosted game). I don’t know how to explain that in simple terms. What I mean is that Godot Engine kind of mixes the term peer-to-peer.

Atleast that’s how I understand how the multiplayer API works.

So your code is correct, but I would recommend getting any connetion errors and handling them. Both create_server() and create_client() return OK or ERROR.

Also, yes you can spin up a single dedicated instance for each lobby if you like. And if that floats your boat, go for it. However, you could also store a Dictionary of players, and assign each instantiated ENetMultiplayerPeer object to them, and then handle all the routing inisde one instance of the game - and use only one port.

1 Like

Thank you! I guess I will go with having single dedicated instance for each lobby.

But also, about your idea on having Dictionary with all instantiated ENetMultiplayerPeer:
there is quite important instance property called multiplayer. As far as I am aware from documentation it’s part of TreeNode. Which would technically mean that for each new lobby (to keep everything as one port in one dedicated server instance) I would need to have multiple scenes with multiplayer setup. I am not sure about that though but if it’s true then that would be quite painful process to setup. But it’s just a my thought on this.

Note: The instance multiplayer is really important because:

# for both server and client
multiplayer.multiplayer_peer = enet_network_peer;

# for dedicated server
multiplayer.peer_connected.connect(_client_connected);
multiplayer.peer_disconnected.connect(_client_disconnected);

Anyways, I will mark your answer as a “Solution”. Thank you so much for responding! To be honest, I just wanted to be sure if in terms of security and production is this really a good idea to have something like this… and well it seems like it’s actually the good way to have something like this.

But perhaps having in the server firewall large range of opened ports 7000-7100 may pose a security risk. Perhaps, I would lower range to 7000-7030; so only 30 lobbies allowed. This limitation probably would kind of give more performance to the server because running large amount of lobbies (let’s say 100 lobbies) would be a hard time for the server.

1 Like

This thread is still open, though, If other people have their opinions and ideas on this.

Hello. I’ve just noticed a huge flaw in my system. I am not sure if it’s considered a flaw but whatever.

To “update” my game I would also need to “update” my Dedicated Server running processes for each lobby. This kind of complicates things. Especcially, when my game is for android mobile devices. Updating android games usually happen through Play Store. How to make that Dedicated Server with downloaded player’s version will be always the same I am not sure…

Perhaps, I can make some kind of check. Each released game instance would have an version. I would just compare the versions of the clien’t game with the Dedicated Server lobby process version. If two versions are not equal to each other then prevent player from connecting and ask him to update his game.

Also there is other thing with a solution. Solution would be to somehow save all the players on the specific lobby ids → then kill all these running processes of each lobby → re-run these processes of each lobby using the new update Dedicated Server executable (basically, just replace the old version executable with the new one) → re-connect all players to their specific lobbies they were playing.

Well, basically it’s just how any type of such “Lobby System” would work.

I don’t know though how to make is “seamless”. Like I would first upload update to Play Store then upload update to my server. Server would kick all players and when it appears to be that their client game versions are not the same as the server one then they would have to update their games…
Such solution would kind of… idk destroy the statistics of player base? Like imagine during each update suddenly the amount of players playing my game drops to 0 and then rises again after everyone downloaded the new update.

You push the change to the players first and make sure it’s backwards compatible. Then update the servers. But there’s always going to be downtime.

1 Like

I were thinking more about this to make more comfortable gameplay for players and I’ve came up with something…

After pushing update to the Google Play, I will push update to my Dedicated Server instances for the lobbies.

Pushing update to the lobbies is the neat part. Instead of shutting down the process of Dedicated Server that processes the lobby, I would create a text file. Text file would contain value true.
Basically, because my game primarily relies on round system, after the round ended the Dedicated Server process will check if there is such a text file. If there is then the Dedicated Server process will kick all the players from the lobby and shutdown itself (stop the process with the stopcode 0).

But before shutting process down the Dedicated Server would do HTTPRequest to my server API url “domain.com/update_lobby”. This API url is for updating the lobby settings and the lobby status (active, closing, archived, waiting - the waiting part is when lobby was first created and at first it has 0 players so instead of shutting down the django web application knows that the status is waiting for the lobby owner to join so it will not shut down the empty lobby)

Django web application would receive that API url which has the updated status of the lobby “waiting” so the lobby will not shutdown due to 0 players count. After 5 seconds the update executable of Dedicated Server Godot Engine instance is being run with specific port for that lobby and specific lobby_id of that lobby.

Because all the players from the lobby were specifically kicked by the Dedicated Server, all the players will reconnect to the lobby by their own clients. What I mean by this? Well, there would be special “kick codes”. Let’s say if "kick code” is 300 then:

  1. When the player was disconnected from the lobby, his client game will wait for 8 seconds to rejoin the said lobby. (we know that the lobby is now updated because 5 seconds passed during which the new updated process of the lobby was started).
  2. Player will also not be able to click any buttons and there would be displayed special message on his main menu screen with this content: “Reconnecting automatically to your lobby. Please wait…”.

I haven’t tested this if this will actually work. Perhaps, some changes will be made for the practical implementation. Atleast, that’s how I’ve structured how this would work and in my opinion it is currently good idea of implementation of the part when it is needed to update the game.

This message is for the people who also interested in how to create lobby system and have stumbled upon this problem.

I think it’s great that you’re coming up with solutions to problems, but I would caution you about offering advice to future readers for things you haven’t implemented yet. The industry standard is blue-green deployment. You have two identical servers. You only use one - the green server. If that one fails for any reason, you fall back to the second, blue server, which immediately become the green server. You then fix the broken (now blue) server.

Likewise, when you update your game, you update the blue server. You then swap it to become the live server. Depending on how you’ve architected your server, the users may experience a disruption, but if you’re using your DB and connections are going through a router, you can literally change the game mid-game and users won’t even notice unless new features are visible.

In larger overhauls where the system must come down for breaking changes, you let players know about an outage and do it when most are sleeping.

1 Like