Since I seem to be incapable of sleeping, here's a quick description of how networking works in Productive...
Productive has a traditional "lobby" interface, where the children who are gathering to play the game can configure their player characters (and eventually the game-size and in-game name). This interface is handled by the olpcgames.mesh interfaces.
olpcgames.mesh creates a PygameTube ExportedGObject on every client. You can either make broadcast statements to the whole game-room (via a signal) or call methods on an individual client to "whisper" something to that person. There are only a small number of message types involved in a lobby screen and at the moment all of ours are simple "I'm a fuzzy" or "I'm an octy" type messages.
Most of this logic is implemented in the ui module, in the SetupScreen class. The character chooser widget participates by tracking the individual user's current settings (though currently those settings are discarded after the page completes).
The other thing that the lobby interface does is that it informs all users of who the server will be for the game. At the moment we just always assign the person who shared the activity as the server. There's no reason for that, and we may code up a widget that lets a group decide fairly who will be the server for a given game at some point (including allowing the game to continue even if the original host leaves). There's a proposed algorithm for that in the docs directory
Once the host clicks "Start Game" in the lobby, the lobby screen is replaced by the game screen and the main networking operations begin. Productive's main game uses a simple client-server model, with all clients requesting changes and the server doing whole-world updates when changes occur.
The game class has two methods getNetworkServer(tube) and getServer(handle) which respectively create/retrieve the NetworkServer and create/retrieve a client proxy to that network server on the server machine.
The tube we use to create the NetworkServer is actually the same tube as was used to implement the lobby. We just pass in the olpcgames.instance().tube value. Because we must have gone through the lobby to get to this point, we know that the instance() (and thus the tube) must already be there. The handle we use in getServer is the handle of the person who first declared themselves "SERVER" in the lobby channel.
We define only 3 significant entry points for the NetworkServer. Two of them are methods that will be called (asynchronously) by the WorldRender widget when the user's interactions indicate a desire to move or produce a unit. The use of asynchronous networking (that is, using callbacks) is crucial if you want to avoid "hanging" your activity while waiting for a network timeout. (This is actually an area where we have a thread-boundary violation, incidentally. Eventually we'll need to make sure we have the gobject lock around those calls.)
The third entry point is the SendUpdate signal. This signal is defined with a reasonably complex method signature because it is sending the entire game-state across the wire. We pull the signature together from the individual record definitions so that we can share the definitions across the application. Each client's GameScreen subscribes to the SendUpdate signal when the screen is connected. The callback for that signal simply updates the stored state and produces a NULL Pygame event to kick off a Pygame rendering cycle.
And that's it. As with most complex libraries, Telepathy is quite simple to use once you've done it and know where to hook things up, but it seems to take quite a while to understand enough to start using it. Hopefully this little description will give you an idea of how to get started.
Pingbacks are closed.