Advertisement

creating basic lobby services

Started by November 20, 2014 10:25 AM
18 comments, last by hplus0603 9 years, 10 months ago

Hi all, I have seen some simular threads here but not one that specifically gives me the info i'm after.

I would like to create a fairly simple barebones (to start with) lobby server. I have an existing, finished game that was originally on Xbox360 and used the live services. I've since ported it to pc and rewrote the network using Lidgren - it's finished and working. One player can host and another can connect to the IP and play the game. Now I want to create and host a proper lobby server, I have a pretty good idea what i'm doing but wanted to get some opinions before I charge in blindly!

What i'd like to create is a service I can use for ALL games I create - think of it as a mini (reallllly mini) battle net. So the server will be running, through admin controls i'll setup "games" it knows about with unique id's and once a client has connected and registered what game it belongs to the client will be able to send request packets for game listings (i figure a list of gameid + some string to identify it in a human way, be that host name or some name the hoster has set for his game room) and then later request full game details (ip, etc) by passing one of the ID's. At it's most basic level /thats it/.

No multiplayer game logic, no real communication with the players once they've started their private game session (except perhaps something to say a game is in progress or has finished and remove it from list). It should be quite light. Later on I will add leaderboard services (which my mobile apps can also use simply for online scores) and probably lobby chat - i'd like players to be able to chat in the lobby before whatever game they are playing actually starts.

Sorry for the waffle tl;dr etc but I wanted good clear explination of my intent. Here's where I want to get advice before I storm in: given I want this to be an expandable system for use in my current and FUTURE games and that any dev's hope is that their games are successful what do I need to watch out for and how should I handle the storing and sending of data in a way that scales and doesnt die if I have say 5 games using the server that are all moderately successful with thousands of players wanting to host and search for games each? Even simple game listing and basic chat can start to become an issue when 10k clients are attempting it. I sort of figured I might store games in progress and games availible to join in a sql database? or would that be a bad idea? Just in ram would be faster to access but that could get large, fast. Should I be looking at some sort of compression for chat strings to minimise packet size?

Any advice at all would be greatly appreciated! I could run in and "just make it" but i'm sure i'll get got by something later on.

Incidently i'm most likely to write this in c# - though c++ is also an option.

Do you want a single server process to be able to handle a multitude of games? If so, each request would need to have a game identifier. It might be simpler to say that you run a different service (on a different port/host) for each different game.

Typically, you'll want login information to go with lobbying. I e, if a user says "I'm Britney Spears," you have to make sure that that user is, in fact, uniquely registered as Britney Spears; typically using a password. And, once passwords/logins are involved, you either get into sophisticated key exchange schemes (which are harder over UDP,) or you end up first establishing a session key using TLS/HTTPS, and then verifying that key once provided on the UDP session. I recommend the latter :-)

So, you'd want some service that accepts username-and-password, and provides session-key. That service also accepts username-and-session-key and returns "yes" or "no" (for use by the lobby.) Alternatively, the session key can contain the user name and a signature using a shared secret with the lobby server.

The lobby then accepts username-and-session-key, and then accepts host-game-parameters or find-game-parameters, and provides updates to the client based on whether matches are found. A nice variation is if the client provides general-game-parameters, and the lobby server first finds someone hosting; if not found, it turns the client into a host, and makes it wait for other clients -- that way, a player doesn't have to decide whether to be host or client. When the wire protocol tells the client whether to host or be client, you can then later make "smarter" decisions on who hosts (based on reputation, networking, payment plans, etc) without changing the client software.

Additionally, you'll probably want the lobby server to provide NAT punch-through introduction services.

Finally, you want a log of things-that-happened to come out of both the session server and the lobby server. That way, you can debug and audit the operation of the system.
enum Bool { True, False, FileNotFound };
Advertisement

Firstly: thanks for your reply, some good information there! :)

I'm intending to host this service on the cloud (microsoft azure). I am part of the bizspark program so for a couple of years I get $150 of azure credits every month so it provides the perfect platform for testing the viability of this software I think! I was intending to have a single server to handle a multitude of games as it seemed to fit well with what I understand of cloud computing where an application can scale and be granted (or removed) extra resources as and when required.

NAT Punch through is definitely something I should look into and had not considered.

The truth is I know that right now I can sit down and write this software straight away and i'd have no concerns if it was just for a single game because if I do something inefficiently it would be unlikely to ever show with todays computing power and high speed internet etc. But with this goal of being able to, for sake of argument, "infinitely scale up" adding more games each with their own player base I find myself questioning myself and what I know and what is the best way to store data (despite the fact I know SQL is used for HUGE performance critical programs, silly me!) and wonder what sort of pitfalls there are that I have never considered!

I'm going to go google for this TLS/HTTPS session key method you suggest for authentication - do you possibly have any links to useful articles on it or any subjects related to this task that you think I should read?

Thanks )

cloud computing where an application can scale and be granted (or removed) extra resources as and when required


Note that your application will not scale across hosts in the cloud unless you explicitly build it to be federated. Is that a goal of yours? Federated matchmaking is really, really, hard. And not needed for any game you're likely to create on your own :-)

do you possibly have any links to useful articles on it


I wrote a chapter in one of the Game Programming Gems books about it.
A web page variant of that chapter is here: http://www.mindcontrol.org/~hplus/authentication.html
enum Bool { True, False, FileNotFound };

really, really good read! I feel pretty confident about tackling user authentication now, thanks! Infact overall I feel pretty good about prototyping this server now and giving it a spin. I do have one last question for you though:

Given that this servers job is ONLY to register and list hosted games and later perhaps provide lobby chat and some other basic lobby functions - what sort of CPU power and ram am I going to be aiming for? Azure lets you be very specific in what you need when it comes to number of cores and amount of ram. With most of the data stored in a DB instead of active memory im thinking lots of ram isnt a requirement, but i've never really considered how CPU intensive network traffic is *scratches chin* what do you think?

The main things you need are authentication, game-selection and/or matchmaking, and facilitating NAT punch-through. This could all be done as a RESTful service. I would suggest that the easiest way to design such a thing would be to have the lobby service deal with only one game, but you can run multiple instances of the service bound to different network endpoints to service multiple games.

Depending on how forward-looking you want to be, the next version of Windows server will support containerization (similar to linux containerization LXC, as popularized by Docker) which would probably be the best long-term solution of all. Azure will support this as soon as its ready. A container is essentially a way of running an application or service is a way that's completely isolated from other containers, and hence you can treat the system as if you're the only one running -- each container can have its own dependencies that might otherwise conflict with those required by other containers, for instance. It has the advantages of a VM but is lighter-weight because you're only using the resources required for the application and its dependencies, not a distinct OS stack for each application. You can do this on azure today if you wrote the service for linux, since you can run Linux on Azure. The benefit of containers is that you can test locally the same exact container that you will run in the cloud, or toss the container over to whoever provides you the best value for hosting containers. Google and lots of other huge web-services companies have (or are) adopting containers like mad because of their benefits for DevOps, they really are the wave of the future.

throw table_exception("(? ???)? ? ???");

Advertisement

hmm, containers sound very interesting!

I learned a long time ago to not just ignore the advice of experienced dev's just because it doesn't match my original idea and a prevailing theme from everyones replies has been to have a seperate instance of the lobby server for each game using it. I'm really taking that on board but I have a few questions specifically about it :)

My original concept for the one instance was having a sort of start-it-and-leave-it-alone server that through a control panel (be it local or through a web interface) I could add "game" instances to it which it would give a UID to identify and I could configure some basic information such as if I add achievements later on a list of such achievements for that game, etc etc. Later on I might add a very basic friends system so people could see when their friends were online and what game they were playing using this lobby. I can still do all of that with seperate instances as long as they share one common database of course.

But what if I said that in the back of my mind is the vague idea that if this works and prooves stable with my games that I may in the long term offer the service to other small indie devs who need a bare bones framework for game discovery in their multiplayer games but are not using steam or LIVE and can't afford the starting cost for services like smartfox (and do not need the excess of features!) or the VM/Cloud subscription fees? Would you still suggest a seperate instance for each game? That could become.. quite alot of instances if it took off!

If I keep as one instance of the server, reading and storing data to a SQL db and then sending it to connected clients (but never doing game logic and having nothing really to do with games that have been "started" except an occasional poll to see if they still exist if game requires "game in progress" to be visible for additional players to join) then what sort of problems should I expect to encounter? Programmatically its a very simple thing to add multiple games to the server, it's just one more level of structure (ie instead of "foreach gameroom" it becomes "foreach game { foreach gameroom } }") so I am assuming that people suggest a seperate instance for each game for phsyical reasons such as processing and memory constraints? What else do I need to know?

So many thanks from me to everyone involved in this discussion, it's been a treasure trove of information :)

facilitating NAT punch-through. This could all be done as a RESTful service.


NAT punch-through does not work over REST because of the low-level networking details involved -- basic NAT actually uses UDP! Even TCP NAT cannot be done REST-fully, because it relies on persistent connections and re-using local port numbers.

Game matchmaking CAN be done REST-fully, assuming that you're OK doing long-waiting on a match. I e, the client would issue a HTTP request for a game match, and the server wouldn't return the match until a match was actually made, stalling the request 1-20 seconds while searching.
The reason you don't want to do multiple REST requests for matchmaking, is that you'd run into situations like this:
1) client issues REST request to "put me into the queue"
2) client's supposed to poll every so often for "did I find something"
3) clients queue entry gets match to a game
4) however, client shut down, and server doesn't know about it, so other players are matched with a zombie player

Matchmaking is not a great match to the REST model. It's real-time data -- it's not cacheable. REST works best when data has some amount of cacheability lifetime.
enum Bool { True, False, FileNotFound };

Sorry, "facilitating" was too vague -- I merely meant exchanging whatever information was necessary to begin NAT punch-through between the peers, not performing punch-through itself. In practice, that's probably not much info at all.

As for REST, I would think it would be sufficient to set the response to not be cached, no? True, its then a polling API, but you can code around the dropped-client issue, and the system should be robust against that anyhow. I imagine I would have it poll after a handful of seconds, and the response would contain the current matched players; you'd keep getting the current player list until all were ready to go, and only then would you get the host info when it was immediately ready to go. REST/polling is probably pretty chatty though, I agree.

throw table_exception("(? ???)? ? ???");

As for REST, I would think it would be sufficient to set the response to not be cached, no?


Yes, that would "work," with the draw-back I suggested.
In general, it turns out that REST is a good match to services where data is at least in some sense cacheable.
When caching is not useful and real-time is part of the equation, REST is often less good of a match.
Thus, thinking about caching is a good proxy for thinking about whether REST design makes sense for whatever you're building.
(There are other design paradigms: messaging, RPC, etc -- good design means using the right paradigm for the right problem.)
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement