Search Unity

Multiplayer with dedicated server: let’s discuss best practices

Discussion in 'Multiplayer' started by K1kk0z90_Unity, May 5, 2019.

  1. K1kk0z90_Unity

    K1kk0z90_Unity

    Joined:
    Jun 2, 2013
    Posts:
    90
    Hi all, :)

    I’ve been programming games for years, but I’m still a newbie when it comes to networking. I want to start this thread for newbies like me, to try helping each other with development of a multiplayer game with a centralized server architecture, and to discuss best practices and ask for feedback to experts.
    I’ll start by briefly describing my current approach to multiplayer with dedicated server, kindly asking for feedback, and asking few questions.

    I have developed a prototype for a very simple turn-based strategy game (like Checkers). I have implemented real-time multiplayer using the Telepathy library (which uses TCP), using a fully authoritative server architecture: the server keeps the game state, computes game logic and notifies clients; clients only get player input and send commands to the server.

    In my implementation, the server-side code is separate from client-side code, but it is still inside the Unity project. I have a Server scene which contains only a game object with a GameServer script attached, which keeps listening for clients’ connections: when two players have connected, it starts a new match and stops accepting new connections until the match is over or one client has disconnected.

    When I want to make a server build, I only include the Server scene and I make a Windows or Linux headless build (by checking the “Server Build” option). When I want to make a client build, I include all other scenes and build for iOS or Android.

    Is it a good approach? What is yours?

    Here are some questions that I have:
    1. With my current implementation, there can’t be more than a simultaneous match on the server: for the purposes of a prototype (as it currently is in my case), this is acceptable, but obviously it’s not for a real game. How do I manage more concurrent matches? Should I create a new thread for each match and always keep listening for new connections in the main thread? But doing so, if the server crashes, ALL matches crash as well. So, maybe spreading matches among different server processes would be a better idea. Furthermore, this would allow me to use more than one server machine. But this approach would need an additional layer in my server-side architecture, to route connection requests to game server instances. What is a good way to implement this?
    2. Where should I host my game server(s)? I have tried to deploy a Linux server build of my game on an Amazon AWS EC2 instance. I managed to let it start on the remote machine, but when I tried to connect with my game client to the remote server, it didn’t work: the game server didn’t receive any connection request. Maybe I should have done something on the remote machine to expose the port on which the game server was listening? However, I have tested the game by deploying a Windows server build on my PC and letting an iOS and an Android device play together by connecting to the server on my PC in LAN.
    Thank you in advance for any help: even a little tip is precious for me to learn. :)
    I hope that also other network programming newbies like me will find this thread useful to discuss best practices and learn from experts. :)
     
    reelie likes this.
  2. gamevanilla

    gamevanilla

    Joined:
    Dec 28, 2015
    Posts:
    968
    Your prototype's approach is reasonable indeed.

    1. An independent process per spawned game server instance is a good approach. The additional server-side layer you are referring to is usually named master server and there are many variations on it. We use it in our Master Server Kit for UNET and you can also find some information on how Photon Server does it here.

    2. You will definitely need to make sure the port used by the game server instance on your remote server is available from the outside. Details on how to do this will vary depending on your service provider; at the very least, you will need to make sure said port is open.
     
    K1kk0z90_Unity likes this.
  3. bartofzo

    bartofzo

    Joined:
    Mar 16, 2017
    Posts:
    151
    Hey there,

    We're using Amazon Gamelift for dedicated server hosting. It's basically EC2 but with a lot of extra options tuned to hosting games.

    To answer question 1. With Gamelift you can host multiple game sessions on a single EC2 instance and they will run as separate processes, so if one should crash, the others should be fine. They offer a comprehensive API that will cover all your needs to route players to games etc, though it will require extra code that runs in the cloud to manage all of that. For that we're using serverless Lambda functions.
     
    K1kk0z90_Unity likes this.
  4. doctorpangloss

    doctorpangloss

    Joined:
    Feb 20, 2013
    Posts:
    270
    This is an excellent question!

    How, pray tell, does a server written in Unity, which can only run one scene at a time, play like, two games at the same time?

    It doesn't!

    How does every multiplayer game service run a multiplayer Unity game server? Do they really, as one poster said, run "one process per match?" And then what, do they coordinate running processes on some machines somewhere?

    No, they don't!

    From a best practice point of view, every contemporary (2012+) popular multiplayer game for has an embeddable game server. What does that mean, embeddable? It's like a DLL.

    You have a "Server.dll" that looks like this:

    Code (CSharp):
    1. var match = new Match(gameConfiguration, playerConfigurations);
    2.  
    3. foreach (var player in players) {
    4.   Pipe(player.InboundMessagesStream.Select(DecodeGameMessage).ToStream(), match.InboundMessageStreams[player.Id];
    5.   Pipe(match.OutboundMessagesStream.Select(EncodeNetworkMessage).ToStream(), player.OutboundMessagesStream);
    6.   Pipe(match.OutboundPrivateMessagesStream[player.Id], player.OutboundMessagesStream);
    7. }
    8.  
    9. match.Play();
    You can create as many instances of a Match as you want. The thing that creates Match instances is responsible for "piping streams," i.e., network connections.

    Inside the match's Play, there's code calling a "Game.dll"

    Code (CSharp):
    1. void Play() {
    2.   var game = new Game(GameConfiguration.SimulationConfiguration, PlayerConfigurations);
    3.   game.Play();
    4.   Pipe(game.GameEvents, playerOutboundMessages)
    5.   foreach (var (playerId, playerInboundMessages) in PlayerInboundMessageStreams) {
    6.     // ...
    7.   }
    8. }
    So your game design engineers actually author a "Game.dll" and your server engineers make a complete superset of functionality called the "Match.dll," and a game design engineers never have to worry about the specifics of the networking, which is basically the opposite of what everyone says but makes a ton of sense when you look at real games.

    Maybe you're Epic, and yes, you run a match per process. UE4 started getting written in 2003! Seriously, think contemporary. Historically League of Legends was match per process, but I don't think they could afford to waste that many resources. Most first person shooters are match per process, but a new FPS engine hasn't been written in a very long time. Besides, an FPS is fundamentally not a game you can make with Unity. And if you don't understand why yet, ironically, you still shouldn't be writing one on Unity.

    There are exceedingly few games you can make with Unity where the internal game logic needs to be aware of the network representation of the game. Networked first person shooters and Rocket League do need the network representation to be good, and those are basically impossible to make with Unity. And yet, every asset store multiplayer framework forces you to do things this way, which is insane and wrong and why nobody uses multiplayer frameworks successfully to deploy games without essentially rewriting all the functionality anyway (ask the e.g. Ultimate Chicken Horse guys why it took them two years to add networked multiplayer to their very successful indie game).

    The #1 best practice for Multiplayer Games That Actually Ship: Separation of concerns. The place that comes up the most is your ability to make your game embeddable. If it's embeddable, it's realistic to reuse all the web technologies designed to manage stateful servers competently for your own game.

    You need to prove that you can play your strategy game with mocked opponents in a unit test outside of Unity, if you're committed to C#. Once you've done that, you've actually already architected your game in a best practices way.
     
    Last edited: May 6, 2019