sjbrown's Writing Games Tutorial

This guide assumes a certain level of knowledge. If you find it confusing,
either you should brush up on some of these concepts, or I should become a
better writer. It kind of puts us into an arms race of laziness.

Design Patterns are a communication tool; they do not dictate design, they
inform the reading of the code.
This guide makes use of the Design Patterns "Model View Controller" (MVC),
"Mediator", and "Lazy Proxy".
Time won't be spent describing these patterns in detail, so if they sound
foreign to you, I recommend checking out the book
"Design Patterns" by Gamma et al. or just surfing the web for tutorials.

It's always a good idea to sketch out your game either with pictures or text
before you begin coding.

We will start by trying to create a program where a little man moves around
a grid of nine squares. This is a overly simple example, but easily extensible
so we won't get tied up in the game rules, instead we can focus on the
structure of the code.

The choice of MVC should be pretty obvious where a graphical game is
concerned. The primary Model will be discussed later under the heading
The Game Model. The primary View will be a PyGame window
displaying graphics on the monitor. The primary Controller will be the
keyboard, supported by PyGame's internal pygame.event module.

We haven't even got to the Model yet, and already we have a difficulty. If
you are familiar with using PyGame, you are probably used to seeing a main
loop like this:

In this example, the Controller (the "Handle Input Events" part) and the View
(the "Draw Everything" part) are tightly coupled, and this is generally how
PyGame games work, at every iteration of the main loop, it is expected that we
will check for input events, update all the visible sprites, and redraw the
screen.
Experience tells us that as the code grows, this section will get hairy.
Organizing this in an MVC pattern, we separate the View and the Controller.
Our solution is to introduce a Tick() function that the constantly looping
main loop can call for both the View and the Controller. That way there will
not be View-specific code in the same location as Controller-specific code.
Here is a rough example:

Readers with some experience writing games may be balking at this point,
thinking MVC is more complex than necessary, and that it will add
unneeded overhead, especially when the goal is to create a simple,
arcade-style game. Now historically, arcade games were just that, games
written for arcade machines. The code ran "close to the metal", and
would squeeze all the resources of the machine just to get a 3-color
ghost to flash blue every other frame. In the 21st century,
we have resource-rich
personal computers where applications run a couple layers above the
metal. Hence, organizing your code into a pattern has a small relative
cost. For that small cost, you get the following advantages: more easily
add networking, easily add new views (file loggers, radars, HUDs,
multiple zoom levels, ...), keep the Model code "cleaner" by decoupling
it from the view and the controller, and I contend, more readable code.

Let's examine the infinite while loop in the last bit of code. What is its
job? It basically sends the Tick() message out to the View and the Controller
as fast as the CPU can manage. In that sense it can be viewed as a piece of
hardware sending messages into the program, just like the keyboard; it can be
considered another Controller.

Perhaps if "wall clock" time affects our game there will be even another
Controller that
sends messages every second, or perhaps there will be another View that spits
text out to a log file. We now need to consider how we are going to handle
multiple Views and Controllers. This leads us to the next pattern in our
architecture, the Mediator.

We implement the Mediator pattern by creating an EventManager object. This
middleman will allow multiple listeners to be notified when some other object
changes state. Furthermore, that changing object doesn't need to know how many
listeners there are, they can even be added and removed dynamically. All
the changing object needs to do is send an Event to the EventManager when it
changes.

If an object wants to listen for events, it must first register itself with
the EventManager. We'll use the weakref WeakKeyDictionary so that listeners
don't have to explicitly unregister themselves.
[TODO: more weakref rationale. gc, etc]

We will also create an Event class to encapsulate the events that can be sent
via the EventManager.

class Event:
"""this is a superclass for any events that might be generated by an
object and sent to the EventManager
"""
def __init__(self):
self.name = "Generic Event"
class EventManager:
"""this object is responsible for coordinating most communication
between the Model, View, and Controller.
"""
def __init__(self ):
from weakref import WeakKeyDictionary
self.listeners = WeakKeyDictionary()
#----------------------------------------------------------------------
def RegisterListener( self, listener ):
self.listeners[ listener ] = 1
#----------------------------------------------------------------------
def UnregisterListener( self, listener ):
if listener in self.listeners.keys():
del self.listeners[ listener ]
#----------------------------------------------------------------------
def Post( self, event ):
"""Post a new event. It will be broadcast to all listeners"""
for listener in self.listeners.keys():
#NOTE: If the weakref has died, it will be #automatically removed, so we don't have #to worry about it.
listener.Notify( event )

Here is a rough idea how this might be integrated with the previous code.

As we get more and more listeners, we may find that it's inefficient to spam
every listener with every event. Perhaps some listeners only care about
certain events. One way to make things more efficient is to classify the
events into different groups.

For the purpose of this guide, we'll just use one kind of event, so every
listener gets spammed with every event.

If you try to use this particular Event Manager class for your own project,
you might notice it has some shortcomings. In particular, if a block of code
generates events A and B sequentially, and a listener catches event A and
generates event C, the above Event Manager class will process the events in
the order A,C,B, instead of the desired order of A,B,C. In the later
examples, you can see an example of a more advanced Event Manager that
always delivers events in the desired order.

Creating a model, we need to go through a process called "abstraction".
We have played games before, so we have a valuable mental library of concrete examples of finished products similar, in principle, to the finished product we want to create.
If we can find abstract commonalities in those concrete examples, it will help us create classes to organize our code, make it flexible, make it maintainable and give us a vocabulary to talk to other team members about the code.
There are many possible abstractions we can come up with and judging whether we have created good abstraction is very subjective. It's important to keep your goals in mind and also anticipate how the requirements could possibly change in the future.

Here is a Model that has worked for me and is general enough to adapt to many types of games:

A Player object represents the actual human (or computer) that is playing the
game. Common attributes are Player.score and Player.color. Don't confuse it
with Charactor. Pac Man is a Charactor, the person holding the joystick is a
Player.

A Charactor is something controlled by a player that moves around the Map.
Synonyms might be "Unit" or "Avatar". It is intentionally spelled "Charactor"
to avoid any ambiguity with Character which can also mean "a single letter"
(also, you cannot create a table in PostgreSQL named "Character"). Common
Charactor attributes are Charactor.health and Charactor.speed.

A Map is an area that Charactors can move around in. There are generally two
kinds of maps, discrete ones that have Sectors, and continuous ones that have
Locations. A chess board is an example of a discrete map. A 3-dimensional
level in Quake (with floating-point precision), or a level in Super Mario (with pixel-precision) are examples of continuous Maps.

In our example, the Map will be a discrete Map having a simple list of nine
sectors.

A Sector is part of a Map. It is adjacent to other sectors of the map, and
might have a list of any such neighbors. No Charactor can logically be
in between
Sectors. If a Charactor is in a Sector, it is in that sector entirely, and not
in any other Sector (I'm speaking functionally here. It can look like
it is in between Sectors, but that is an issue for the View, not the Model)

In our example, we will allow no diagonal moves, only up, down, left and
right. Each allowable move will be defined by the list of neighbors for a
particular Sector, with the middle Sector having all four.

You'll notice that in the figure, Item is not explicitly connected to
anything. This is left
up to the developer. You could have a design constraint that Items must
be contained by Charactors (perhaps in an intermediate "Inventory" object), or
maybe it makes more sense for your game to keep a list of a bunch of Items in
the Game object. Some games might call for Sectors having Items lying around
inside them.

The code can be downloaded here:
example.py
Or read it in your browser
here

This example makes use of everything covered so far. It starts out with a
list of possible events, then we define our middleman, EventManager, with all
the methods we showed earlier.

Next we have our Controllers, KeyboardController and CPUSpinnerController.
You'll notice keypresses no longer directly control some game object, instead
they just generate events that are sent to the EventManager. Thus we have
separated the Controller from the Model.

Next we have the parts of our PyGame View, SectorSprite, CharactorSprite,
and PygameView. You'll notice that SectorSprite does keep a reference to a
Sector object, part of our model. However we don't want to access any methods
of this Sector object directly, we're just using it to identify which Sector
object the SectorSprite object corresponds to. If we wanted to make this
limitation more explicit we could use the id() function.

The Pygame View has a background group of green square sprites that
represent the Sector objects, and a foreground group containing our "little
man" or "red dot". It is updated on every TickEvent.

Finally we have the Model objects as discussed above and ultimately the
main() function.

Our next task will be to make the game playable over the internet. Eventually
this will lead to multiplayer capability for our game, but it's important that
we do the single-player network step first, as it exposes us to
several constraints that may affect any future code.

The code in the following sections is written incrementally, so don't expect
to just take the code from the first section and write a game with it.
Subsequent sections sometimes address problems with the previously shown code
and explain how to overcome those problems.

One of the goals of this tutorial is to show how game development can be done
rapidly. Usually anything involving networking is anathema to "rapid"
because once you introduce networking, you introduce multiprocessing, latency,
error handling, and general hair-pulling. A principle of the code examples
in this tutorial is to make sure that the game can be run without even turning
on networking. The networking feature should have zero impact on the code
in example.py -- running the game in single-player mode should not execute any
networking-related code paths. By being strict about this separation, we hope
to make it possible to develop the game rapidly, no matter what snags
the networking code may introduce.

Computer processes (usually there is only one process of interest on each
physical computer, or "host", so we just use the term "host") that communicate over the network can be organized in many ways.
In games there are three popular ways to structure hosts, Peer-to-Peer, "Strict" Client-Server, and "Servent" Client-Server.
The driving factor when deciding how to structure network hosts for games is usually trust.
Games are emotional (well, good ones) and competitive, players are motivated to win, and when they don't win, they want to trust that no other player had an unfair advantage.
To ensure trust, there needs to be a consistent, authoritative model.

In the "Strict" Client-Server structure, there is one "3rd party" server that
all the clients connect to. Any change to the authoritative game model must
happen at the server. A client can predict the authoritative state, but it must not put faith in game state until it hears from the server that that is, in fact the case.
An example game would be World of Warcraft.

In the "Servent" Client-Server structure, one of the players, usually the one
that starts the game, acts as the server as well. This suffers from the
drawback that other players trust the game state as much as they trust that particular player. However no 3rd party is needed.
Examples can be found in many first person shooter games.
This structure is often paired with a 3rd party "matching" server that connects
players with each other and then hands off to the Servent host.

In the Peer to Peer structure, all hosts have identical roles. The great
benefit of a Peer to Peer structure is that it robustly deals with network
disconnects from individual hosts. However trust is compromised.
Trust can be bolstered by adopting token passing strategy such that the host holding the token acts as a Servent.

When you call a function that requires network communication, it may take a
long time to finish. If we waited for network-dependent functions to
finish before we called the functions which draw the graphics, the
users would get angry and flame us on internet message boards.
The solution is to write functions that send out messages over the
network and then return immediately, not waiting for a reply.
The replies will eventually come from the remote hosts
and wait in a queue until our process can service them. It is important to
remember that the replies may not queue up in the same order as the requests
went out.

This asynchronous quality is fundamental to network-related code. Luckily
designing our code such that there is an independent EventManager and
well-defined events will make dealing with asynchronous messages
from the network fairly painless.

This tutorial will use the Twisted framework for network-related code. I
recommend reading the Twisted documentation, though it should not be
necessary to get through this tutorial. (note, a lot of the Twisted
documentation focuses on writing servers where the client implementation is
unknown. I recommend skipping forward to the sections on Perspective
Brokers)
The ideas presented here should
be independent from the choice of Twisted; the examples could just as well be
implemented with raw sockets or carrier pigeons.

Twisted is a framework that hides the queue from us, it expects the
programmer to call reactor.run(), which is a mainloop that consumes the
queue and fires off callbacks. The callbacks are provided by the programmer.

We are already reaping the benefits of the MVC pattern.
By changing only a small amount of code, we no longer have a Pygame display, instead the TextLogView just prints received events out to the console.

Another thing we don't need in a server is keyboard input, so we can remove
the KeyboardController. Where do input messages come from instead? They come
from the network, so we'll need a Controller object for the messages sent by
the clients, NetworkClientController.

The NetworkClientController instance is a special object that can be sent
across the network via Twisted's Perspective Broker mechanism (because it
inherits from pb.Root). The remote client will request a reference to the
NetworkClientController instance, once it has received it, it can call any
method that starts with "remote_". So for the client to send messages to the
server, we have implemented remote_GameStartRequest and
remote_CharactorMoveRequest.

It could be tempting to make all of the objects remotely referenceable. (ie,
inherit from pb.Referenceable) The problem with that approach is that it
tightly couples the networking code with the rest of the code. It's preferable
to separate the networking code so that the other objects just use the event
passing strategy described by the Mediator pattern.

In our examples, we're only going to have one class in the server that is
referenceable, and also only one class in the client.
[TODO: expand on this]

We also don't need the CPUSpinnerController in the server, so we've
removed that, and replaced it with Twisted's reactor, which similarly
provides a run() method.

It is not necessary to understand the Twisted parts of this, you can
just consider them "magic". What you should know is that invoking
reactor.run() causes the mainloop to block while listening on port 8000.

If we play some dirty tricks, we can see what our server does without
writing a client. Instead, we will just connect to it using the Python
interactive interpreter. Now, reactor.run() is a blocking call that does
not return until the reactor is shut down, so in order to get back to
the interactive prompt, we have to crash the reactor
and then call reactor.iterate() in order to communicate with it.
It should go without saying that this is not a recommended practice.
Also, if you replicate the session below, you may have to call iterate()
multiple times before you see any result.

The code for server.py can be downloaded here:
server.py
Or read it in your browser
here

King of the Castle

As seen above, Twisted's reactor object is designed to be in charge of the
main loop. This creates some difficulty, as we've already got a main loop
in the CPUSpinnerController. We could subordinate Twisted's reactor, and
"pump" it on every iteration of the CPUSpinnerController's main loop, but
that has the drawback that we need to abuse Twisted's API in a way that was
probably not intended, and may not be forward-compatible.

Alternatively, we can use Twisted in the intended way and then use the
LoopingCall class to make the firing of the Tick event reliant on the
reactor's main loop. Creating a LoopingCall object is a way of asking the
reactor to call a function repeatedly. The downside of this approach is that
games often start out in single-player mode and we don't want to invoke any
network-related code like Twisted unless the user chooses a multiplayer option.

The source code to the remainder of this section is
in multiple files. You can download them in tar.gz format here:
example2.tar.gz, or browse
the source code
here.

The previous example of a server gave a good introduction to the basic
networking technique, but it's a little too simple for our purposes. We don't
really want to write a new function for every message the server can possibly
receive. Instead, we'd like to leverage our already existing Event
classes.

This brings us to one of the most important parts, but possibly the most
tedious part of implementing networking. We need to go through all the
possible events and answer these questions about each:

Do we need to send it from the client to the server?

Do we need to send it from the server to the client?

Are there security issues with sending this data over the network?

Is the data formatted in a way that it can be sent over the network?

If we must, how do we reformat the data so that it can be sent?

(Eventually, one might also ask "How often will this message be sent?" and
therefore "How can I best optimize this message?")

While there are many ways of doing this with Twisted, I will outline a
strategy that tries to minimize the amount of code written (to combat the
tediousness of this task) and to maintain the separation of the networking
requirements from the remainder of the code.

Using Twisted, we must do three things to a class to make it possible to
send instances of it over the network: make it inherit from
twisted.spread.pb.Copyable, make it inherit from twisted.spread.pb.RemoteCopy,
and call twisted.spread.pb.setUnjellyableForClass() on
it [TODO: ask someone who knows Twisted if
that's really necessary]. Things
can become even more complicated when we consider questions 4 and 5
from our list above -- does the data require special formatting to send it
over the network? The only data that doesn't require special formatting
are the
literal
types: string, int, float, etc., None, and containers (lists, tuples, dicts)
thereof.

While examining the Events, two cases will occur, either it will not
require reformatting, and we can just mix-in pb.Copyable and pb.RemoteCopy, or
it will require reformatting and we will have to create a new class that has a
routine to change the original data into something that can be sent over the
network. [TODO: link to explain Mixins
somewhere]

In this next example, we've split the code into multiple files. All the
events are in events.py. In network.py, we try to answer all of the above
questions for each event in events.py. If a message can go from the client to
the server, we append it to the clientToServerEvents list, and likewise for
the serverToClientEvents list. If the data in the event is simple, like
integers and strings, then we can just mix-in the pb.Copyable and
pb.RemoteCopy classes and call pb.setUnjellyableForClass() on the event.

On the other hand, if an event contains data that is not network-friendly,
like an object, we need to make a replacement event to send over the wire
instead of the original. The simplest way to make a replacement is just to
change any event attributes that were objects to unique integers using the
id() function. This strategy requires us to keep a registry of objects and
their ID numbers, so that when we receive an event from the network
referencing an object by its ID number, we can find the actual object.

It is very important that these classes are named exactly the same as the
class they're replacing but with a prefix of "Copyable".
We can see how to replace the original events with these network-friendly
versions in NetworkClientView.Notify in server.py and we can see how the
receipt of these events is handled in PhonyModel.Notify in client.py.

We've seen that we can send fake messages to the server via an interactive
python shell, but what we really want is a graphical client. There are a few
steps to realizing this goal. Firstly, the client(s) need to be notified of
any changes to the state of the server. So we'll need bidirectional
communication. Not only does the client send requests to the server, but the
server also notifies the client of events. (This is why one-way ("pull")
protocols like XML-RPC or HTTP are not well suited to our needs)

From the server, changes need to be sent out, so we need to create a new
View on the server.

The NetworkClientView keeps a reference to the server's registry that maps
object ID numbers to the actual objects. It also has a list of clients. The
objects in the clients list inherit from pb.Referenceable, so we can use the
callRemote() method, sending messages over the network. The
serverToClientEvents list is imported from network.py.

NetworkClientView.Notify() is primarily interested in Copyable events. The
event passed in to Notify() might already be Copyable, due to the mixing in of
pb.Copyable in network.py. In that case,
isinstance( ev, pb.Copyable ) returns True. If it's not Copyable,
there still might be a replacement class in the network module, and we can
check by prepending "Copyable" to the event's class name because we used that
naming convention for the replacement classes in network.py.

As can be seen in NetworkClientView.Notify(), the server expects the client
to send it a remotely accessible object (like one that inherits from Twisted's
pb.Root) when the client connects. Thereafter, the server can use that object
to notify the client of events.

Now we'll (finally) get started on the client. From the point of view of
the client, the incoming messages from the server represent a Controller, so
we've got a NetworkServerController class in client.py. As you might be
expecting, the client will also send events to the server through a View, the
NetworkServerView.

On the first TickEvent that the NetworkServerView gets, it attempts to connect
to the server. When the connection is made, the Connected() method is called
with a reference to a server object that inherits from pb.Referenceable,
therefore the client can use it to remotely access the server. It also creates
a ServerConnectEvent.

The NetworkServerController gets notified of that ServerConnectEvent and uses
it to pass the server a reference to itself. Now the server can call the
remote_ServerEvent() method of the NetworkServerController. So both the server
and the client have references to remotely callable objects. This is the
channel through which they communicate.

Theoretically, there should be only one model, the authoritative model on the
server, and the clients should just be Views and Controllers for that model.
However, it is no simple matter to
keep references to remote model objects in the client's EventManager,
Views and Controllers. Also, many events can be handled
entirely on the client side, and always sending them to the server would
create needless noise. So a good design should create a local model to mirror
the game objects that exist on the server side.

We will create a PhonyModel on the client side whose state we will keep in
sync with the authoritative model on the server.
This PhonyModel provides the same interface as
the server's model, but it has a special role - to ensure
that the local game objects do not change the game state when they don't
have the authority to do so.
In our example, this is accomplished by keeping two EventManager objects,
one called phonyEventManager, which just discards events that it receives,
effectively silencing all events coming from the local game objects, and
one called realEventManager, which propogates events received from the server.
Events posted to the realEventManager will show up in the View objects, events
posted to the phonyEventManager will not.

Because our example is very simple, we can get away with this simple
implementation. One can imagine situations where we might want to allow a
local game object to change the local state. This could be accomplished by
making PhonyEventManager propogate these special events. Another approach
could be to not have a local Model on the client, only a
View object on which incoming events from the server had a direct effect.

Here's the tricky part: how do we send complex objects like Players or
Charactors over the channel we've created? This is called
serialization. To serialize our objects, we need to do two things.

Create a registry that maps unique IDs to game objects

For each class, have a way to change all of its internal data into
numbers and strings, and a way to change those back into useful objects

Getting unique IDs is easy, we can use the result of the id() function when
called on the object in
question on the server. It must originate from the server so that it is
unique, otherwise we'd have multiple IDs for a single object.

When events referencing complex objects get to the NetworkClientView on the
server, the objects are serialized starting in the constructor of the Copyable
event.

As you can see, the server won't send the actual object when it sends the
event, it will only send a unique integer ID. It also makes sure that there is
a mapping from that ID to the actual object in the registry.

When the client is sent the CopyableCharactorMoveEvent, the PhonyModel
picks it up (the PhonyModel is the only object interested in events that start
with "Copyable").

When a charactor moves, he is in a new sector. To communicate this to the client,
the server sends a CharactorMoveEvent, which has one attribute, the charactor
himself. The client receives this event, sees the charactor referenced inside, and
requests the new state (which sector is he in?) for that charactor.

This is a very generic approach to solving the problem.

Server: Hey, dude X just moved!

Client: Oh really? Tell me everything you now now about that guy.

Server: Well, he's in the "ACTIVE" state, and he's in the sector with the unique ID 123567.

Client (to self): Ah, excellent. I already know about that sector, so I'll just change my little model of the universe and move that dude into that sector.

This conversation took 4 messages. It could have been shorter; we could have
just hand-crafted the CopyableCharactorMoveEvent into something
more specific to our game's needs, for example we could have included the sector
as an attribute of the event to obviate the request for more information.

Server: Hey, dude X just moved! To sector 123567!

Client (to self): Ah, excellent. I already know about that sector, so I'll just change my little model of the universe and move that dude into that sector.

But we'll keep the code very generic for now. A lot of the other events will follow
the same pattern.

Back to the code snippet, if the client has already received that object from
the server, self.sharedObjs.has_key() will return true, and it
can grab a reference to the object from the registry and carry on as normal.
If it hasn't received that object yet (as is the case the first time this event
is received), it must first create a placeholder object, and then copy the state
of the object on the server into this new placeholder object. It does this by
calling GetObjectState() with the unique ID of the needed object.

GetObjectState() basically just finds that object on the server (in this
example, the Charactor that has moved), and serializes it's data with a call
to getStateToCopy(). GetObjectState() returns the dict and the object ID that
was requested.

# from client.py#----------------------------------------------------------------------
def StateReturned(self, response):
"""this is a callback that is called in response to
invoking GetObjectState on the server"""
objID, objDict = response
if objID == 0:
print "GOT ZERO -- better error handler here"
return None
obj = self.sharedObjs[objID]
success, neededObjIDs =\
obj.setCopyableState(objDict, self.sharedObjs)
if success:
#we successfully set the state and no further objects#are needed to complete the current object
if objID in self.neededObjects:
self.neededObjects.remove(objID)
else:
#to complete the current object, we need to grab the#state from some more objects on the server. The IDs#for those needed objects were passed back#in neededObjIDs
for neededObjID in neededObjIDs:
if neededObjID not in self.neededObjects:
self.neededObjects.append(neededObjID)
self.waitingObjectStack.append( (obj, objDict) )
retval = self.GetAllNeededObjects()
if retval:
# retval is a Deferred - returning it causes a chain# to be formed.
return retval

In the simplest case, "success" is True, and GetAllNeededObjects() returns None
immediately. Then the next callback, CharactorMoveCallback gets called, and we're
done.

However, if "success" was False, that means more data is needed to complete the
originally requested object's state. The PhonyModel keeps a list of neededObjects
that must be requested from the server before the originally requested object is
complete. Each of these needed objects may also append to the neededObjects list
for subsequent objects they need. So when we call GetAllNeededObjects() the
recursive behaviour begins.

# from client.py#----------------------------------------------------------------------
def GetAllNeededObjects(self):
if len(self.neededObjects) == 0:
#this is the recursion-ending condition. If there are#no more objects needed to be grabbed from the server#then we can try to setCopyableState on them again and#we should now have all the needed objects, ensuring#that setCopyableState succeeds
return self.ConsumeWaitingObjectStack()
#still in the recursion step. Try to get the object state for#the objectID on the top of the stack. Note that the recursion#is done via a deferred, which may be confusing
nextID = self.neededObjects[-1]
remoteResponse = self.server.callRemote("GetObjectState",nextID)
remoteResponse.addCallback(self.StateReturned)
return remoteResponse

As you can see, another call is made to GetObjectState on the server that will
result in StateReturned being called. Notice that this isn't truly recursive.
GetAllNeededObjects doesn't block. It returns immediately. But it returns a
Deferred object, remoteResponse. So the original Deferred had it's first callback
called, and that returned a new Deferred object. This is called
Chaining Deferreds
and it causes the first callback to block until the second Deferred's callbacks
are finished. Hence we get recursion over the network.

Here is a flowchart that summarizes the actions taken when the client gets
an event containing a complex object.

Notice that we must make sure that the event we send over the network has
enough information to update the client with any relevant changes to the state
of the server. The client may already have a local version of an object, but
if that object has changed, the client still has to call
GetObjectState(), as is demonstrated with the CharactorMoveEvent.

With that in mind, a question is raised: where do we put the intelligence
do determine what object states we need to retrieve? Right now, we've put all
this logic in PhonyModel.Notify() [TODO: is this the best
place? what about inside Copyable events?]

You can download the example that handles fetching
complex objects here: example3.tar.gz.
Or browse the source code here.

The previous discussion is a good start and provides some useful code. I
encourage you to play around with it and see if you can get your game sending
objects back and forth. As your code becomes more complex, you will run into
some more problems:

What if we don't have enough information to call __init__ for
some attributes in setCopyableState() ?

What if we don't know the specific subclass for an attribute in
setCopyableState() ?

To clarify, here's an example of when an issue like this might come up.
Lets say we write a game where two Penguins fight each other. Each Penguin
has a weapon, and every weapon is initialized with a name, like "Deathbringer"
or "Destroy-o-Matic", or "Daffodil".

We'll start off by creating a 2-player game that runs locally, not over the
network. Our loosely coupled architecture allows us to do this, and it's a
great advantage to be able to develop your ideas first and worry about network
issues later.

We will add a couple new events, PlayerJoinRequest, PlayerJoinEvent (the
Player object is no longer created by when the Game is constructed), and
CharactorPlaceRequest. The KeyboardController is also modified to detect
new keypresses, p and c to fire off those request events, and
the o key to switch between active players. (see screenshot above).

You can try this out by running python example.py from the
example4.tar.gz archive below. When it starts, press p twice to
request 2 PlayerJoin events, then press space bar to start the game, then
press c to place one character, o to switch to the other
player, then c again to place the second character. Direction keys
move the charactor around, as per usual.

We want to ensure that Player One's client cannot control Player Two's
charactor. We want the server to reject any
request where the player instance contained in the request is not an instance
the sender is allowed to control.
As a first step, we need to be able to uniquely identify clients. Then we
need to map clients to a set of Player objects (or more commonly, just one)
that they are allowed to control. Then we need to filter out any events that
should not be allowed based on that map.

Luckily, Twisted provides a rich set of tools to identify clients, aka
"authentication".
Most of this is explained in
[TODO]Authentication with Perspective Broker
in the Twisted docs. I'll go over the specific usage in our example,
but you should also review those docs.

Our first change will be to change the server's NetworkClientController from
a pb.Root object into a pb.Avatar object:

As you can see, the class now inherits from pb.Avatar, and the methods
that were previously named remote_BlahBlah are now named perspective_BlahBlah.
Also, the NetworkClientController objects will need to keep track of their
realm and their avatarID. The realm is basically a factory on the server that
gets requests for new client connections, and creates new NetworkServerViews
and NetworkServerControllers for each successful connection.

If you look at the body of the requestAvatar method, you see where the network
views and controllers get created. The requestAvatar method is also where
the avatarID comes into play. It is created internally to Twisted, and passed
to our code. It is an identifier guaranteed to be unique for each
client. Effectively, it is a "username".

requestAvatar gets called as a result of calling login() during the
AttemptConnection method of the client:

Now that we've got these usernames dictated by Twisted, we might as well
use the information in our Model.
[TODO: expand...]

All that remains is changing the KeyboardController. The KeyboardController
keeps track of which player is "active" and controls only that player,
switching when the "o" key is pressed. That works fine when running as a
single process, but now that there are a couple clients and which player
a client controls is regulated by the server, we need to adjust the
KeyboardController.

First we'll give the constructor an optional argument, "playerName".
By making the default value None, we can check to see if it's set, and
anywhere it's not set, we keep the single-process behaviour.
[TODO: paste in code]
The only change to make is the reaction to a PlayerJoinEvent. In
single-process mode, it makes sense to always control the new player,
but with multiple clients, that new player could have come from a remote
host and the server won't let this local host control it. So only try
to control players that match the playerName.
The next question may be "where does the playerName get set then". It
is simply during the main() function of the client code.
[TODO: paste in code]

[TODO:
I need a section here on why to chain deferreds when the client receives
events from the server. On a received event, the client starts getting new
state information from the server. Because of the asynchronous nature of
networked programs and the choice we made to not send ALL the needed
information at once, there are points in time when we've collected incomplete
information from the server. If we populated our phony Model with that
incomplete information and then the User Interface got Ticked, it will likely
result in a crash or at least a UI bug. So we chain deferreds, get all the
state information we need, and once it's all collected, then we update our
phony Model and post events.
]

[TODO:
I need a section here talking about how to refine the client code so that
you don't need a waitingObjects queue. Basically, with a better Placeholder
class, and some Python self.__class__ = foo magic, we don't have to keep
a queue and solidify the Placeholders after everything has been downloaded.
]

As is wont with the internet, sometimes a connection gets accidentally
dropped. It's always nice to let players reconnect. The key to achieving this
is a GameSync message.

A GameSync is a request from the client to pull sufficient information
about the game to recreate its current state from nothing. In our example,
we just send the Game object from the authoritative model.
Start by creating a new event, GameSyncEvent:

Add another remotely callable method on the server, and the
server-side code is done:

# from server.py
class NetworkClientController(pb.Avatar):
...
def perspective_GetGameSync(self):
"""this is usually called when a client first connects or
when they reconnect after a drop
"""
game = sharedObjectRegistry.getGame()
if game == None:
raise Exception('Game should be set by this point')
gameID = id( game )
gameDict = game.getStateToCopy( sharedObjectRegistry )
return [gameID, gameDict]

Next we need to hook up the client side. When should a GameSync be requested?
It needs to be done at a time when the client has a connection to the server,
but the client-side model (PhonyModel) has not yet been populated. A good place
is the ServerConnectEvent handler in PhonyModel itself.

The last detail of the reconnection involves example.py. After a client
reconnects, it will have a fresh KeyboardController object. This
KeyboardController won't receive a PlayerJoinEvent to set up it's activePlayer
because the game is already underway (in the authoritative model, both players
had already joined). So we add some new code to example.py (compromising
the principles set out in the Rapid Development
aside, but it'll be harmless, I promise.
(can anyone suggest a better way to do this?)
) to take a GameSyncEvent and figure out which player the KeyboardController
should control.

At this point you can test reconnecting. Extract the code from
example4.tar.gz, and open up 3 terminals. In one terminal, run server.py.
In terminal 2, run `python client.py user1`. In terminal 3, run
`python client.py user2`. Create a player in each Pygame window. Then start
the game by pressing spacebar in a Pygame window. Create a charactor in each
Pygame window, and move the charactors around to new positions. Then close
the second Pygame window. You should see the server react by printing some
messages about the disconnection. Now run `python client.py user2` again.
The client should connect, get the game state, and display the charactors in
the same positions shown in the first Pygame window. You should be able to
control charactor 2 again.

A widget is the elemental object in a GUI. A widget can be a button, a label,
a text entry field, etc. A widget can even contain other widgets, like a
toolbar, or a menubar, or even a simple horizontal box.

You can get as complicated as you like when creating your GUI engine, but
this tutorial will focus only on some simple widgets. Here are the ones we
will implement:

label

button

text-entry field

[TODO ... scrollbox?]

All widgets share a small amount of behaviour, so we have an abstract Widget
class that inherits from Sprite. Widgets can be focused and unfocused, and
have a 'dirty' flag so that they can be redrawn when needed, and not on every
single call to update().

A button is also a very simple widget. It is just an image that can be
clicked, and when it is clicked, it fires off an event. For simplicity's sake
the image is just some rendered text, but it could be anything.

A text box is a little bit more complicated but still easy to understand. It
is basically a rectangle into which text can be typed. When it gets focus it
shows a little vertical bar (|) and starts responding to keypress events.

Above is a diagram showing some common uses of a Graphical User Interface
in games. In each of the above screens, there is a blue section representing
buttons or other widgets. It will also serve as the idea for our next example
application, "Fool The Bar".

The Menu GUI is the first thing seen when the program
starts up. It is usually just a collection of buttons, most often things like
"New Game", "Quit" and "Options". Sometimes there are multiple kinds of
"Options" choices.

The Main GUI is where the game actually gets played. Few
games have much in common when it comes to the Main GUI. However, many games
have a "function bar" or a "shortcut bar" along some edge that is made up of
buttons or other widgets.

A Cutscene is part of the game where direct control
is taken away and part of the game's story is presented. This can be done by
playing a movie, or by presenting text with accompanying pictures. User input
is usually limited to a few choices like "skip" or "continue".

A Dialog is one of the more tricky things to do in a game.
It is usually a rectangle that pops up over the Main GUI containing buttons,
text, or other widgets (like an RPG "inventory" dialog). While the Dialog is
up, the presentation of the Main GUI is usually not interrupted (though it
could be). Things still may move around in the background, but the Dialog is
understood to have focus. For instance, if a "chat" dialog is present,
the keypresses that usually make a Charactor move (ie "WASD") will now go only
to the "chat" dialog so the user can type in a message. If a "yes/no" dialog
has popped up such that the "no" button is over a charactor on the screen, and
the user clicks the "no" button, that click should not select the charactor
underneath, it should only press the "no" button.

Here is the source code to our example application,
Fool The Bar: foolbar.tar.gzWARNING: OLD CODE

Why do you use from module import *? Don't you know
that's bad coding style

Yeah, my bad. I promise i'll clean all those up before i declare
this "finished".

Why don't you use Twisted's Cacheable? It seems like you're just
reimplementing it.

I am a bit of a newbie when it comes to Twisted. I read a bit
about the Cacheable stuff, but it seemed to me I'd have to make the classes of
my Model be Cacheable, and I didn't want the network code to be coupled with
my model code. And seeing as I would have to write serialization code even if
I used Cacheable, I just bulldozed ahead. Would it be possible to use
Cacheable without coupling it with the Model code? Could someone who is
familiar with Twisted point me in the right direction?