Advanced Java Game Development: State Data

Thursday Jan 27th 2000 by David Fox

This second installment focuses on designing game state as an object, deciding how much of the state to actually send over the network, what to store in the game client, and how much responsibility to give the server.

Part 1 of this series: Advanced Java Game Development: Introduction

Strip away the fancy graphics, graceful animations, streaming TCP/IP sockets, and eardrum-beating sound effects, and you'll notice that games — no matter their genre or complexity — amount to nothing more than a pile of bytes. Every player's move and every artificial intelligence decision eventually expresses itself as a change to this core game state data.

Java makes it quite easy to keep an abstract notion of game state.

Ideally, the game state should be a central repository that tells the animation component of your game what it should draw and advises the networking part of your game what to send through the wires. State will be changed and accessed a lot, and thus should be as tightly written as possible.

Java makes it quite easy to keep an abstract notion of game state. Just create a class with all the data structures you need, tap in methods to access or change that data, and you're off and running. By designing state as an object, various parts of the state can quickly be accessed and altered.

Once you've written out the game logic, you'll want somebody to play with. That's where networking comes into play. The core Java libraries make it easy to communicate over the Internet. It's possible to set up an extremely dumb and simple TCP/IP client-server system within minutes. Just create a socket, listen for a new connection, connect the client to the server, and you're off and running.

The big challenge to you, as a game developer, is deciding how much of the state to actually send over the network, what to store in the game client, and how much responsibility to give the server.

Peer-to-Peer Pressure

The first and perhaps most obvious approach is to forget about servers altogether. If your game involves two players competing, why have a middleman? Just have the first player send her IP address to the second player, create a connection, and then communicate. In other words, treat both clients like their own mini-servers.

This is known as peer-to-peer communications. The beauty of this approach is that there's no server to build or maintain. Also, since there's only one network jump — from one peer to the other — the speed is nice and quick. But that's assuming there are only two peers. If you have three peers, your speed gets cut down exponentially, since everyone must be able to communicate with everyone else. Four or more people in the mix, and your game begins to feel some heavy pain. Since every peer is reliant on data from the other peers, you're only as fast as the slowest connection. One glitchy or sluggish client can ruin the whole show. That's peer pressure.

There are other problems, too. Without a server keeping track of things, peer to peer connections often fly out of synch. If one of the clients gets confused about the state, you'll have a hard time figuring out who's in the right. This makes it easy for cheaters and hackers to come in and modify the network packets and ruin everyone's day.

It only gets worse. If your game is Tic Tac Toe, you're probably willing to live with the client doing all of the tough work. However, for a huge multiplayer real-time strategy universe, your client will quickly get bogged down with artificial intelligence calculations, physics modeling, data synching, and kick-butt graphics rendering.

Wait, it gets even worse! There are two more spoilers: Firstly, peer-to-peer networking doesn't work via applets -- an applet can not accept connections from remote servers, due to security restrictions. Secondly, peer to peer generally doesn't work over firewalls. The snafu goes like this: Most firewalls mask the client's real IP address with a fake address. If you try to talk directly to the real IP address, the firewall will rudely block any traffic. You can try to require that anybody who wants to play your game open up a specific port for communications, but that becomes an imposition and is not possible on many corporate setups.

Fat Client, Server on a Diet

Okay, so you decided you need a little more server in your life. But there's no need for a server to have everything in it but the kitchen sink. You might want to investigate putting all the game rules on the client and just crafting a small, dumb server that sits there watching the world go by, happily passing along any messages. This type of server is pretty easy to write, and should take up little of its host's processing time or memory.

If you create a good network gaming protocol and a resilient enough client, you could use the same client for a number of games.

The only worry here is that, just as with peer to peer, the client may be biting off more bytes than it can chew. Clients will have to do all the computing work and resolve any conflicts. Once again, it's easy for one client to cheat, or get confused, or send along totally illegal moves.

Also, what if you want a more advanced gaming table? At PlayLink, Inc., for instance, we try to run a friendly sort of place, letting people drop in at any time and play, observe, or wait around to play the next game. Assuming that the game's players manage to stay synchronized, who'll pass along the latest game state to newcomers? If you have one of the clients do that work, you'll have to be ultra-sure that the client has the latest state. It's easy to get wires crossed, and have out-out-date states passed back and forth, tripping up everyone.

Dumb Client, Absolute Server Power

Given all the forehead-scratching hassles that a client might deal with, maybe the best idea is to have the server do it all. The client could be nothing more than a rendering engine that the server controls. This hearkens back to the good old days of mainframes and null terminals.

If you create a good network gaming protocol and a resilient enough client, you could use the same client for a number of games — Tic Tac Toe, Chess, Backgammon, card games, etc. Just have the server send down the graphics and the locations at which to paint those graphics. The client, meanwhile, could keep track of mouse clicks and keyboard presses and immediately send that info to the server.

There are a lot of great reasons to do something like this. It's nearly impossible to cheat, since the server figures out all the rules — you can easily tell if somebody is clicking where she shouldn't be clicking. You're always perfectly in synch, since the server knows exactly what's going on, and when. This setup also makes it horribly easy to update the game rules and fix gameplay issues — one small fix on the server affects every client in the world.

Sounds great. For game universes where security is key, such as casino games that bet with real money, this is the only way to run the show.

The real boon of a well-designed Java system is that you can migrate from one server architecture to another with relative ease.

However, for more casual gaming, there are loads of reasons why this type of architecture sucks. First of all, things couldn't possibly be slower. Every time you click your mouse you'll need to wait for the server's feedback before anything happens. Granted, the client can be written so that it guesses what will likely happen. But you're still talking about one far-off authority that must dictate all actions. On slow Internet connections, that makes for a pretty horrific gameplay experience.

In addition, the server will work up quite a sweat. It must store every game's state, calculate all moves based on game rules, and constantly accept and fire off a hailstorm of network traffic. A server that does all the work will obviously get overworked more quickly than a more streamlined system.

Husky Client, Husky Server

What about sharing the work between client and server? A server, after all, is a good, warm, central place to keep game state. Think of the server like a vast vault that holds all the stuff you don't want to mess with. As long as the server has the latest game state, you'll be able to help out-of-synch clients get back on track. You'll also be able to recreate the precise game state for any observers that pop in during the middle of play.

The client, on the other hand, is very people-oriented. It's great at reacting immediately to user input, handling game rules, and drawing snazzy graphics.

Specifically, the PlayLink server handles the following:

  • Chat messages.
  • Who is in the game, and what each person's status is — player, waitlister, observer, or AI. If a new person jumps into the game, the server alone determines whether there is an empty slot for the person to play at. If not, it puts that person on the wait list.
  • The gametable's options.
  • An array of bytes representing the game state, such as the current score, whose turn it is, and the exact arrangement of the game board.

The real work involves deciding when to update the state. In general, the sooner a client can send the state to the server, the better. Every time a player makes a move — even before the move is animated — the client should figure out how it affects the state. It then sends this state change to the server, which stores the info and broadcasts it to the other players and observers as quickly as possible.

Now, this solution is not a perfect one. It's still relatively easy for a dishonest client to cheat by implementing illegal game rules. But for a casual board-gaming system such as PlayLink, this compromise seems ideal.

Serve Me Now!

No matter what client-server architecture you choose, you'll always need to make further choices such as: whether to use TCP or UDP, what your proprietary gaming protocol should look like, and whether to encrypt the network data for added security. You'll also have to decide how to distribute tasks among different clients, and if you really want to think big, you'll need to figure out a way to balance load between multiple servers.

In any case, the real boon of a well-designed Java system is that you can migrate from one server architecture to another with relative ease. Just swap out a few communication listener classes with a new messaging protocol, move around your game rule classes, and recompile. Is it ever that easy in practice? Heck no. But one can always hope.

About the Author

David Fox is vice president of Games at PlayLink, Inc. He's also the author of numerous books and articles about cyberculture and technology.

Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved