How the game controller pattern saved my butt.
The Political Machine
Game Flow Architecture
The Political Machine is a new game under development by Stardock Systems. It can be played as both a single player and a multi player game. In order to accommodate both play types in the short time we have allotted to developing the game, I had to think of ways to kill two the birds with one stone. What I came up with is a game flow that worked for both methods as well as a “Controller” pattern that manages the local player, ai logic, and network player messages.
After reading a lot of literature on network gaming, I decided on using a peer to peer configuration. To control game flow, I use a lock step mechanism where the host machine validates each move request and posts the results to each machine via DirectPlay. In a single player game, the local machine goes through all the same validating as the multiplayer setup, but doesn’t post to the network. It sends back “pseudo” network messages to itself and processes them like network messages. This helps to ensure that each machine has the same state and consolidates the game logic to one object, the controller.
In a single player game, the UI and the AI Logic send action requests to the Game Controller. The Game Controller validates the moves and then updates the game world accordingly.
Multiplayer play is just an extension of the single player paradigm. Each machine runs an identical game loop and they communicate with each other via the DirectPlay runtime. The host machine does the extra work of validating each move in its game controller and then posting the results to each machine. The validation is done against the host world state. The host world state isn't modified directly. It is only changed when the requests are validated and the game controller sends the validated moves to the clients.
Since the game system architectures are the same, I was able to write one controller loop with very few conditionals. This minimizes the amount of testing needed. The game flow paths must be tested individually, but he game logic only needs to be tested once.
In the single player game, the human player manipulates the UI. The UI sends formatted game requests to the Game Controller for Validation. If the requests are valid, the controller modifies the game world.
For the AI, the sequence is slightly different. Since the AI logic is in a separate thread, requests are posted to the Game Controller. This frees up the AI thread to continue processing. An event is also passed in the request. After the request is validated and the world is modified, the event is triggered. The AI thread should be looking for this event so that it can send a new request.
The multiplayer flow looks more complicated than the single or AI flow, but it’s really very similar. We added another system to the diagram, but the game flow is the same from a user's point of view. Most of the work is done between the game controller and DirectPlay. One thing to notice is that all the communication between DirectPlay and the game controller is done asynchronously. This is because DirectPlay has multiple threads that can receiver messages and we don't want any of those threads blocked while we processing them. When you look at the diagram, you may think that there's excessive communication happening for the host machine. I chose this to do this because it makes the view from the guest machine look exactly the same as the view from the host machine. This also enforces the lock step paradigm. Both the host and guest game controllers are treated as foreign controllers. Neither of them has the authority to modify the game world directly. Only moves validated against the host machine's game state are passed on to the game controllers. These moves give the controllers the authority to change their local game worlds. In the host's case, they are also modifying the master game world. Notice that once a request is posted to the Host, the controller returns to the UI. This keeps the UI free to process other messages while the request is being validated.
That’s the general overview. There are some tricky parts and spots of genius in the actual implementation. I’d like to show you but I think that’d be giving away too much. You should discover these spots for yourself. I hope you can use this information in your projects. If you have any hints, tips, questions, or comments, please post them in the forums. I’d like to hear what you think and about what you’ve done in your projects..