I have been considering networking options for months, and over the last couple of weeks have had time to explore specific options. I initially made exploratory forays into two disparate implementations: One approach was to write the game logic in ‘inside-out’ form so that everything could be done by callback; the other approach was to put the network code into another process, and pipe data to and from it via os pipes.

The ‘inside-out’ approach would add significant burden to coding the game logic, and was abandoned after an evenings work. The separate process approach was making some headway, but I have put it aside as well, for yet another approach.

What I want(1) is a twisted reactor that can be exited and reentered. I could then use the substantial asset of proven twisted code for various high and low level operations, but use a conventional subroutine-oriented code structure. When an engine needs to exit, it can have the reactor yield to its caller; the caller then follows game logic to set up another engine, which resumes the reactor operation. The timing constraints would be the same as for a call-back, (ie: don’t delay the reactor more than necessary), but the game logic would be more readable and more writable without having to put everything in callbacks.

The usage would be like:

 
class Engine() 
   def iterate(self): 
       # do per frame stuff 
       if self._done:  reactor.release() # yield to engine's caller 
       else:   reactor.callLater(0, self.iterate) 
   def run(self): 
       # init stuff 
       reactor.callLater(0, self.iterate) 
       if reactor.isRunning(): reactor.resume() 
       else:  reactor.run()    

def intro(): 
   # set up intro pages 
   eng = Engine() 
   eng.add(intro_pages) 
   eng.run()   

def room(room_context): 
   eng = Engine() 
   eng.add(room_context) 
   eng.run() 
   return eng.retval   

def main(): 
   intro() 
   # set up lobby context 
   next = room(lobby_context) 
   while next != GAME_EXIT: 
        # set up context for next room 
        next = room(room_context)           

The code is fairly readable, with more specialized function in specialized subroutines.  Calls to the engine can be from main(),  or from a subroutine a few calls deep.  

See my next post for the reactor subclass itself.

1)
OK, what I really want is a file-like connection object:
con = Connection(‘gameserver.org’)
con.write(‘game update data’, game_data)
news = con.read()
check = con.read_non_blocking()
But I am not going to find (or write) that, so I’ll settle for an pause-able reactor.

Advertisements