Andy J Design Game Development and Design

1Sep/091

Multiplayer Game Design: Server-Side Models and Distributed Models

I recently posted about using the MVC pattern in games and provided a very quick and dirty pseudocode model of our hero in MVC.  It showed a nice separation of concerns and had the display removed from the logic of the game.  Shortly after I was prompted by one of my readers about using the Model View Controller pattern distributed over a multiplayer environment.  There are a lot of opinions out there and I'll cover a few here.  I'm curious if you all have feedback on these methods and their use in browser-based games specifically.The simplest arrangement is a turn-based game that takes input from one user at a time and then displays the result to both players.  Thankfully if we put our pattern-based code to use we can alleviate a lot of the client-server traffic in a simple client -server setup.  (For this post I'm going to gloss over the lobby and game initiation part of the process)

We'll start with two simple clients.  These clients exhibit the characteristics of a simple View and Controller pattern.  They have an array of game pieces on them and some simple inputs (lets say buttons).  What these are responsible for is displaying the current game state and notifying the server when input has been given.

The game state is Model and is handled on the server separately from both clients.  Each time one of the players takes action it's sent to the server and the server updates the model appropriately.  Once the model is updated it posts this update to the clients and they are responsible for updating the view.

We'll use a simple game here: player 1 selects their favorite color from a set of three and player 2 selects which one they believe the first player has selected.  Our model stores two values:

  • Player 1's Color Selection
  • Player 2's Color Selection

Our view displays one of 2 states:

  • Select a color
  • Display result

And our controller is responsible for one action:

  • Handle click on displayed color

So the basic gameplay goes like this:

  1. The game model is initialized
    1. Player 1's Color = null
    2. Player 2's Color = null
  2. The model is sent to both clients (both colors null)
  3. The client's views respond by showing the color selection buttons ("red", "green", "blue")
  4. Player 1 clicks "red"
  5. The controller sends this input directly to the server (holding the model)
  6. The server updates the model (Player 1's Color = "red")
  7. The new model is posted to the clients (Player 1's Color = "red", Player 2's Color = null)
  8. The views now reflect new information
    1. Player 1 shows "You selected red, waiting for player response"
    2. Player 2 continues to see color selection buttons
  9. Player 2 selects a color and the controller sends to the server
  10. The new model is posted to the clients
  11. Both clients display the game result screen: "Player 2 guessed the right color!"

So now you can see how we can divide up the responsibility with the model completely on the server side and the controller and view on the client side.  From here we can evolve to some more advanced models.

Gery breached the topic of having multiple models (that is concurrent models) on each of the clients.  This is an important case to consider when moving to realtime gameplay.  In realtime gameplay it's critical for players to be able to act and react based on their opponents' actions.  One reason for moving the model - or part of the model - to the client is so that the model can quickly respond to input and overcome some of the shortcomings of lag and narrow bandwidth.

In this case you have to have a nicely tuned model.  One thing that game developers quickly encounter is the need to move your code from say 5 pixels per ENTER_FRAME event to 5 pixels for 20 millseconds.  This way your game doesn't run faster or slower on different computers.  When you've got your code in this kind of set up you mostly just record input for the controller and pass it on each time the input changes.  A really fun example of this is the game Rabbit Wants Cake developed by one of my favorite Flash game developers jmtb02.

The game is a great example of the effects of input recorded and played back directly to the game controller. To take this to the multiplayer MVC realm we can start with a pessimistic concurrency example.  Two controllers are running (each client) and two models exist.  When input is received from one of the controllers it contacts the other controller and they agree on a timestamp for that input.  That timestamp is then put into the input queue for both controllers.  These controller act on the input at the exact same perceived time and they render the exact same output to the model.  The view remains ideally completely clueless as to the complex multiplayer interaction - the controllers are talking to each other and constant filling the hopper with identical input.

Here's how the pessimistic model works:

  1. A jump button is pressed on client 1 at 56 millseconds game time
  2. The controller sends the event to the other controller and they agree that they can both render it as if it happened at 65 milliseconds
  3. This value is returned and both client models are updated at 70 milliseconds
  4. The view reflects this by making the 1st player's avatar jump!

Unfortunately a pessimistic example of this will lead to a fair amount of lag if the timestamp of the input can't be negotiated in a reasonable amount of time.  In this case we need an optimistic controller that's capable to modifying the model based on historic inputs and some way to reconcile differnces.  This optimistic model means that when client 1 jumps at 56 milliseconds it immediately starts the jump on client 1's model and then sends the jump to the second client telling it that client 1 jumped at 56 milliseconds.  But now you need a much more advanced controller that can figure out exactly what happened between the jump and the current game time and update the model in a fair (and perfectly predictable) way for both models.

The real goal of distributed models like there are to keep them perfectly in sync otherwise your game state differs and it's no longer playable.  With pessimistic concurrency you can see that they will always be perfect but a fair amount of lag is introduced.  This may work for you game!  But for the majority of real-time games you'll need to implement optimistic concurrency and that is a very complicated beast indeed.  Not only must you be able to replay historic events into your existing model flawlessly but you'll also need to find a way to re-sync your models should they get out of whack.  That's a big challenge and we may cover it in the future.

Please don't be shy and let me know what you think!

Share:
  • Facebook
  • email
  • Twitter
  • MySpace
  • Google Bookmarks
  • Live
  • Digg
  • Technorati
  • LinkedIn
Comments (1) Trackbacks (0)
  1. That was fantastic. I’m going to be rereading this post and implementing these methodologies as best I can. What I had been picturing was something simple, like a two car race game, like…. the light bikes from TRON.

    Okay, so you have player 1 and player 2 on their own home computers and the server. I was imagining that the server stores the state of the world, so let’s say that the server changes the map every day, so when player 1 logs on to play he sees whatever the map is for that day, and so would player 2.

    Also, the server stores where player 1 and 2 parked their light bikes from the last time they played.

    So, when player 1 opens up the program/webpage at home, he sees the world that the server is showing that day and where his bike was last parked. When player 2 opens up the program/webpage at home, he sees the world that the server is showing that day and where his bike was last parked, and both players now see each other’s bikes.

    The way I see this playing out in code:
    1) Player 1 opens up the program at home.
    – A LocalGameModel is created that stores pointers to a LocalPlayerModel, a RemotePlayerModel, and a ViewPortModel.
    – A LocalGameController is created that will begin the game loop.
    – A LocalGameView is created that shows the looks of the GUI.
    – A LocalGamePortView is created that shows what’s happening in the game, and is added to the LocalGameView
    – A LocalPlayerView is created that shows the light bike. This is added to the LocalGamePortView
    – A RemotePlayerView is created that shows the light bike of the remote player. This is added to the LocalGamePortView.

    – A LocalPlayerController is created that receives key presses from the local player and updates the LocalPlayerModel.

    2) The Game loop begins and the LocalGamePortView is updating dependant upon the LocalGamePortModel and this cascades down through it’s child views.

    3) Player 1 presses the up button and the LocalPlayerModel is updated by a given amount so that the bike’s model is moved up.

    4) This updated data is sent to the Server’s Model, which then announces an update.

    5) Player 1′s View is updated because it hears the update and player 1′s bike is moved up.

    6) Player 2′s View is updated because it hears the updand and player 1′s bike is moved up.

    Now, problems I see.
    1) Models are usually Singletons, so I begin seeing a problem if there would be more than 1 RemotePlayerModel as now they would overwrite each other.

    2) Again with the Models as Singletons. In this scenario the server is holding pointers to the Models of both players, since players would be of the same type, they would cancel each other out.

    3) There is of course the issue of keeping everything in perfect sync, which I will need re-reading of your post as well as further research into best methods.

    I’m sure there are plenty more issues, I’m just trying to get my head wrapped around how this would work out.

    Thanks again for the GREAT write-up!

    Gery


Leave a comment


No trackbacks yet.