Ideas On Networked Games in XNA

I’ve written networked games in the past, but never in XNA. So after writing the networking logic for Gunstyle I figured I could write a blurb about general networking in games for XNA, which in reality isn’t that much different from any other API. I do not claim to be some expert in networking and I only present to you what I know based off prior experience from a few multiplayer networked games. Oh, and yes I am aware the diagrams are not completely correct UML diagrams, I am simply trying to illustrate a point. This isn’t a How-To guide to networking either, just food-for-thought for those just starting out with networked games.

Introduction

Multiplayer games are great. In fact, I prefer games with multiplayer components more so than single-player experiences (be it online or same console). Games are just more fun with a buddy :). With that said, making a multiplayer game adds a rather large chunk of complexity to sometimes a seemingly simple game. Reasons for that include:

  • Data reliability. You see that variable you just sent over the network to update clients with? yea well, don’t expect it to arrive. In fact, don’t expect anything to arrive :P.
  • Sequencing of events. Have a game event that’s dependent on a prior event happening? I’d recommend avoiding these situations in a networked environment…
  • Testing networked functionality. This is a biggie. It’s really hard to test some features of a multiplayer game without at least one other person (ideally 2 others), or some really well-written unit tests.

When coding a networked game sometimes you have to think differently about the simplest of tasks. For really robust game code, you sometimes have to write it with that 56k’er who’s dropping packets like mad in mind. As opposed to in a single player game, when you declare something to happen in the game it more or less happens instantaneously. The rest of this article will discuss some ideas and share some experience in the realm of online multiplayer games for XNA.

You Reap What You Sow

Trying to tack on multiplayer at the end of a development cycle for a game that was designed from the ground up with just single player in mind is the wrong way of doing things. More often than not the networking will be hacked into the game engine to try to compensate for poorly designed code architecture. Before even writing a single line of code you better be damn sure as to whether this game will have multiplayer or not. Even if it’s a slight possibility of multiplayer, coding your game with that in mind can help tremendously. Now, with that in mind we’ll continue with some basic patterns and designs for creating a multiplayer game.

Architecture

There are several ways to go about designing game engine code to work with networking, but first you have to decide on your network architecture. Will it be the more common server-client model, a peer-2-peer model, or something else all together? Having written similar games using a p2p hybrid and server-client model I can say the server-client model is less of a coding headache in the long run. There’s no doubt a lot of info on coding server-client games and even libraries for it. Just so you have something to compare I’ll just throw out some pros/cons of the Peer-2-Peer model from experience:

Pros:

  • Latency tends to be lower vs. the server-client model
  • Assuming your game can handle many instances on screen at once, your player capacity per game is limited (more or less) to the overall bandwidth of everyone playing, as opposed to the bottleneck being a single server.
  • Can distribute computing power among clients a little easier (due to the way networking is setup). For example, different players can host bot AI so no one player is running all the AI code bogging down their computer.

Cons:

  • Debugging can be a nightmare
  • More stress on the client to be able to upload more data
  • Keeping everyone in sync in a game requires a lot of thorough planning and testing.
  • Security is harder to implement since data is no longer coming from a centralized, controlled authority.

With that in mind, choose your architecture wisely. From experience, the p2p model is cool once you have it working, but you tend to run into several issues regarding firewalls, routers, etc that are simply easier to deal with on the server-client model. And as I’m not a guru on NAT devices and firewalls, I found myself beating my head against the wall more often with p2p. Alright, now onto more C#/XNA specific topics.

Components in Networking

XNA components are a type of aggregate pattern. It has proven very useful in creating flexible code that can be shared among the XNA community. Taking a note from this solution you can employ a similar pattern to Networked objects in a game. The problem we have when dealing with networked games is bandwidth consumption. You want to keep bandwidth low, but playability and believability high.

So you have basic game objects that are networked and they are just spamming data to clients from the server at some set interval. If the game doesn’t have that many networked game objects this could suffice. But once things start getting bigger and more complex you’ll rapidly consume more bandwidth. So what’s a solution? What if we could control the individual update interval of said objects?

Well that’s better. Now you have a finer grain control of how often each object updates. For example, player objects might want to update at a rather rapid interval while an item object could maybe update once every 2-3 seconds. So maybe that’s enough to appease the bandwidth gods. Or maybe not…

Now you may find that 60-80% of your network traffic is coming from only a small collection of classes. Instead of breaking out those classes into separate objects where it wouldn’t make sense, you can use the component pattern to your advantage to have even finer control of what data is sent. We can delegate the sending of different pieces of data at varying rates on a data level rather than per object. The below diagram attempts to illustrate this.

The player object in the above diagram is a netobject which has a netTickList property. With this property you can code custom NetTicker objects that send data relevant to the player. Notice netTicker objects all have an updateInterval property. For instance, in the above example I have a PositionTicker object and a ScoreTicker object. The positionTicker object can be set to a higher interval to update while the scoreTicker can be set very low, since it isn’t absolutely crucial to real-time gameplay. Of course, writing a netTicker for each piece of data isn’t advisable. You can organize the ticker objects where one is dealing with ‘low priority low frequency’ data while have another that is more rapid, which sends things like position, velocity, and health.
Now you have very fine grain control over which data is sent and how often it sends for each individual data set while still keeping your classes coherent and somewhat separating your data sending/receiving from the main class into manageable components. Now let’s take a quick look at how I would setup the abstract class NetTicker.

NetTicker is an abstract class that helps with managing the sending of netEvents (higher level objects that interface with networking layer). Basically the update method handles the timing of when to call the protected virtual method ‘GenerateNetEvent’ and ‘SendEvent’. When subclassing this class all that is needed is an implementation of the latter methods. Simple enough, right? And in case you are wondering how the timing is handled, here is an update method for NetTicker that does just that:

[csharp]
public void Update(GameTime time)
{
_currentUpdateInterval -= (float)time.ElapsedGameTime.TotalSeconds;
if (_currentUpdateInterval <= 0) { _currentUpdateInterval = _updateInterval; NetEvent updateEvent = _GenerateNetEvent(); _SendNetEvent(updateEvent); } } [/csharp] ‘_currentUpdateInterval’ is a private counter that maintains the time and when it hits zero a net event is triggered. As you can see, applying the component pattern with networking can be really helpful in minimizing bandwidth costs. Of course, this is a high level overview of how your game-level code could be organized. I have not and probably will not go into the lower level networking involving sockets, packet fragmenting, handshaking, etc. There’s plenty of resources available on that already and even libraries such as Lidgren.Library.Networking that handle all that already for you.

Silky Smooth Network Play

If you’ve played around with sending player positions over a network you may have noticed the visuals on the clients may not be the most pleasing to the eye, resulting in twitchy/jerky movements. There’s two systems you can implement that will help alleviate the problem. The first is called extrapolation.

Extrapolation is something most will tend to implement on their own without knowing it after thinking how networking latency works. Whenever the server sends data to clients there is a delay between when it’s sent and the client receives it. This is called latency or lag (when netcode performance is sluggish). From my experience, latency is usually measured in round-trip time (referred to as ping). The unit of measure for this time interval differs from microseconds, to milliseconds, to fraction seconds. I’ve usually stuck with milliseconds to keep conversions easy when trying to use extrapolation in the game engine loops.

It’s probably easiest to explain what basic extrapolation is with an example. Say I have a physics engine with a networked physics object moving about on the server. The server sends a position and velocity of object A to a client. The client then receives the data 80 milliseconds (ms) later [note: this is half the round-trip time]. To make the client have a more accurate picture of where object A really is on the server we can fast forward the simulation of object A by 80 ms on the client. Now if our XNA game update loop is set to 60 updates a second that is approximately 16 milliseconds per update. 80 divided by 16 is 5. the object on the server by this time has ran 5 simulation steps. So depending on how our physics simulation is setup we can iterate the object 5 steps ahead or set a delta time for 80ms (probably less accurate depending on equations used) and run it in one large step. Our client-side simulation is now up to date. As we wait for the next update from the server we can run the client simulation on it’s own normal game ticks. The thought process here is if the server and client are in sync then the next update you receive from the server should be pretty close to where the client object is so the jerk is minimal or non-existant. This solves syncronization issues but still isn’t completely smooth.

The next step is implementing interpolation. Extrapolation is great when object movement is predictable through physics equations and has no user input. When trying to network player controls you end up with rather jittery responses because the user can change the direction of the player on a dime (in a side-scroller for instance) and there is no feasible way to predict when this would happen. Adding a layer of interpolation on top of extrapolation can smooth things out, but as a side-effect your clients will always be slightly behind what the server says. This is a slight trade off for visual smoothness. Objects can be interpolated in various different ways. The most basic way being linear interpolation. In this scenario you take the last position you heard from the server [V1] with the newest position you heard from the server [V2] and create a movement vector from these [M1 = (V2V1)]. Then you take the approximate ping time to take a guess at when you’ll hear the next position packet. With this delta time you move the object smoothly from V1 to V2 using M1 as the direction. With this implemented you hope to hear the next position packet ideally when you’ve reached V2. You have to deal with the case where you receive a position packet too early or too late (where the solutions to this issue can differ depending on the game, so for the sake of space and my sanity I’ll skip on this for now). Instead of going into much more detail into this, I’d rather point you to a good article that goes into different and more accurate ways of interpolating objects based off networked packets to get you started.

Going Forward

For those looking for guidance in where to start with creating multiplayer, I hope this article proved useful. If anyone finds any errors in the above information or suggestions/questions please feel free to leave a comment or e-mail me. Right now, networking with XNA is restricted to PC unfortunately, but hopefully with XNA 2.0 many of these ideas will still apply on the Xbox 360. I may write a future article to possibly expand on some of the ideas presented above if there’s enough demand. With that said, happy coding!

4 comments

  1. Great article, Looking forward to more specifics on network on XNA 2.0. I think will open some exciting new network experiences. Another interesting topic would be more discussion about the server side of the problem.

  2. I would love to see you expand on this more. I think networking is one of the most daunting tasks of game development. So, the more information the better.

  3. Thanks for the feedback :). In all likelihood I won’t be expanding on this until XNA 2.0 comes around simply because my networking code may become obsolete, hence why I kept it at a higher level to at least maintain some relevance.

Leave a Reply

Your email address will not be published. Required fields are marked *