Client/Server Sample

This sample implements a simple multiplayer network session. It uses a client/server network topology where each computer sends its player input to the server. The server updates the state of everyone in the session, and sends results to the clients.

Sample Overview

This sample shows the basics of how to create and join network sessions, and how to exchange data using a client/server network topology. Each player controls a tank, which the player can drive around the screen while rotating the turret. The game simulation is run entirely on a single computer that has been designated as the server. Client computers send their player inputs to the server, which then sends the resulting game state back to each client. Only game logic ever runs on the server. Clients function as dumb terminals, responsible only for reading input and rendering the game world as described by the server.

For an alternate way to handle networked game simulation, see the Peer-to-Peer sample. It implements the same tank movement using a peer-to-peer network topology.

Sample Controls

This sample uses the following keyboard and gamepad controls.

Action Keyboard control Gamepad control
Create a session A A
Join a session B B
Move the tank UP ARROW, DOWN ARROW, LEFT ARROW, RIGHT ARROW Left thumb stick
Aim the turret A, D, W, S Right thumb stick
Exit the sample ESC or ALT+F4 BACK

How the Sample Works

In a client/server game, the work of updating game logic, moving objects, computing physics, checking for collisions, and so on, runs entirely on a single computer that has been chosen as the server. With the XNA Framework networking system, the server is just a regular computer that is also playing in the session. This is different from some Windows games that use a separate dedicated computer as the server.

In this sample, the host computer is also used as the server. This does not always have to be the case. Games are free to choose any computer they like as the server (or none at all if they are using peer-to-peer networking). For instance, a game might want to examine the ping times between every computer in the session, and then choose the one with the lowest average ping as the server. The server does not mean anything special to the XNA Framework, so games are free to choose any server. The host, on the other hand, does have a special built-in meaning. The host is the computer that originally created the session. It is responsible for advertising the game session via matchmaking so other computers can find and join it. The host is also responsible for deciding when to move between the lobby and gameplay states, using the NetworkSession.StartGame and NetworkSession.EndGame methods. If you enable NetworkSession.AllowHostMigration, a new host will be selected automatically if the previous host leaves (otherwise, the session will end when the host leaves). The host is always chosen for you by the XNA Framework, but the server is chosen by the game itself. In this sample, the two are always the same; we simply reuse the host computer as our game server. This simplifies our code and removes the need to explicitly choose which computer should be the server.

This figure shows the data flow for a client/server game with three computers in the session:

You can see how all network data is routed through the host, Nimbus, and no packets are ever sent directly between the two clients, Rain and Drop.

In psueduocode, client computers perform the following steps to send their user inputs to the server, then read the resulting game state:

C# 
      foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
      {
      ReadLocalUserInputs(gamer);
      SendUserInputsToServer(gamer);

      while (gamer.IsDataAvailable)
      {
      ReadGameStateFromServer();
      }
      }
    

The server uses this more complicated update logic:

C# 
      foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
      {
      // We can directly read the user inputs for our local gamers.
      ReadLocalUserInputs(gamer);

      // Inputs for remote gamers are read from network packets sent by the client.
      while (gamer.IsDataAvailable)
      {
      NetworkGamer sender;

      gamer.ReceiveData(packetReader, out sender);

      if (!sender.IsLocal)
      {
      ReadUserInputsFromClient(sender, packetReader);
      }
      }
      }

      // Update the game state for both local and remote players.
      foreach (NetworkGamer gamer in networkSession.AllGamers)
      {
      UpdateGamer(gamer);
      }

      // Send the resulting game state back out to our client computers.
      SendEntireGameStateToClients();
    

Client/server networking has several advantages over the alternate peer-to-peer topology:

  • The entire game simulation runs at once on a single computer—client/server games are less likely to suffer from consistency problems.
  • All decisions are made on a single server—it is harder for hackers to cheat by modifying their client game executables.

But it also has some disadvantages:

  • Client/server games usually require more network bandwidth than peer-to-peer games. The server must send information about every player to every client, thus sending a total of (playerCount * (playerCount − 1)) state descriptions. For comparison, in a peer-to-peer game, each computer only sends its own state to its peers, thus sending a total of (playerCount − 1) state descriptions.
  • The network bandwidth and CPU processing workload fall entirely onto a single server computer, which can easily become a processing bottleneck.
  • The network round trip to and from the server may cause players on client computers to experience lag in the game responding to their inputs. (The server does not suffer this lag; hence, the "host advantage" behavior seen in many client/server games.)
  • Because the simulation state exists only on a single computer, supporting server migration if that computer goes away is more difficult than when using peer-to-peer networking.

Extending the Sample

The actual game data transmitted by this sample is extremely simplistic. It sends network packets on every update (60 times per second), and does not perform any prediction or interpolation to compensate for network latency. This works fine over a local network, but is unsuitable for use over the Internet. To make the game playable over the Internet, packets should be sent less often (commonly between 10 times per second and 20 times per second), and prediction algorithms should be used to smooth out the movement of remotely controlled objects.

To get an idea of how the increased latency of packets sent over the Internet can affect gameplay, add this line to the HookSessionEvents method:

C# 
    networkSession.SimulatedLatency = TimeSpan.FromMilliseconds(200);

With this simulated latency in place, notice how the server still sees its tank move perfectly smoothly, but there is now a time delay before clients will respond to user inputs, and the tanks on clients move jerkily because the latency is not exactly the same from one packet to the next. This behavior is quite different to what a peer-to-peer game would experience. In a client/server game, only the host experiences a lag-free response, and the clients see lag on all movement of all objects. On the other hand, in a peer-to-peer game, locally controlled objects always remain smooth, while remote objects suffer from lag.