In my previous post, I talked about how I was starting to implement and test my networking code for multiplayer War of Words. I wanted to take this time to go a little deeper into how it is implemented.
My networking model is essentially a hybrid client/server and peer-to-peer model, although much more peer-to-peer in topology than client/server. In this kind of network topology, each player runs their own simulation of the game on their machine. Each machine periodically sends network messages telling the other machine important information about its state. The receiving machine then updates its remote player with that state information whilst also sending its own information. This works pretty well for most of the gameplay.
But there are problems that cannot be accurately solved with strictly peer-to-peer topology (at least for my game). Take end-of-game detection for example. Each machine runs its own simulation of the game and so it is possible that two different answers to “who won?” can be made. We cannot have this, as there must be one clear winner. Or take random letter generation as an example – both players must be using the same series of letters or else it isn’t really fair. If each player’s game is generating random letters, they will be generating different letters and be completely off. In fact, it will be very, very wrong – the second player will be selecting letters that will most likely be complete garbage on the first player’s machine (because they will be random characters). Clearly, one machine (a server) should be generating these letters and sending them to the other machine to synchronize simulation.
For the random letter generation problem (which is by far the biggest problem to solve), I decided to use another trick to avoid a lot of hassle. This “trick” is to use deterministic random numbers. The .NET Random class takes a seed value and from each unique seed value, it will generate the same random numbers each time (this is why these are called “pseudo-random number generators”). If both player’s games are using the exact same initial seed value for their RNGs, they will then produce the same sequence of random numbers and therefore letters. All that must be done is the server machine needs to be the one to produce the seed value (usually derived from the time of the day) and send that to the remote machine to synchronize each others RNGs. The result is that their isn’t a need to produce streams of random letters and send them over the network. We could do the random seed exchange at the lobby screen or during the loading of the multiplayer game.
Other things need to apply this technique as well. For example, some spells use randomness in computing the effect. The Posion spell, for example, will posion random tiles on the opponent’s board. We will need to ensure that both players compute the same random tile locations when running their independent simulations. This will require me to run through all effects and try to find out which ones are deterministic, and which ones require this deterministic random number generation.
The other big issue of determining win conditions also needs to be solved using a client/server method. In this case, the host (which is always my server) runs the win checking. The remote player never does this check. Instead, the remote player listens for end of game messages coming from the server. It can then be told who won and then place the game into the correct end-of-game state.