[Twisted-Python] pb.Copyable, round trip objects, and untrusted clients

Jasper Phillips jasper at peak.org
Fri May 21 04:39:41 EDT 2004


On Thu, 20 May 2004, David Ripton wrote:

> On 2004.05.20 02:25:20 +0000, Jasper Phillips wrote:
> > 
> > Titan, I presume?  Nice game!
> 
> Yeah.  I don't have time to maintain 50k lines of Java code, so I need
> to rewrite it in 5k lines of Python.  :->

I used to write Java code for a living;  I came to hate it's shortcomings
and IMHO Python is purely superior.  Still, was Titan really 50k lines of
Java?!  Mind boggling.


> > I'm working on a game more akin to Civilization or Age of Wonders, but have
> > basically the same problem.
> 
> That if the Mongols upgrade early from horsies to tanks, Europe is toast,
> but it's hard to make the game dynamic enough to show the Eiffel Yurt?

Heh. :-)  I meant the code. ;-P  Besides, while my game is in the same genre
as Civ and AoW it's more influenced by boardgames and miniatures.


> > For me this was more complex, as what a Player knows is itself a true part
> > of the Game's State, potentially affecting Action/Event resolution.  I end
> > up calculating each Players state myself, and passing them explictly.
> 
> What happens if players conspire to share their secret information offline?
> Is it okay if someone knows stuff but doesn't Officially Know it, or
> does the whole game tend to fall over in the face of such weenie behavior?

It's ok, but won't have in game effects; such information is like secret
info only known to a few.  I'm considering a UI for information sharing, but
that'll just be for allies convenience.


> > Plus this feels to me more like it should be part of the game itself,
> > rather than some oblique networking aspect.  Same end effect though.
> 
> I've decided to have the server and each client save the action stream
> it's seen, which is somewhat similar.  I have code in the previous
> version of this game to deduce and predict which stack hidden enemy 
> units are in based on partial information.

I'm treating event history as game state, although I haven't implemented it
yet.  Not so much for the AIs use, but to model it's effects on future
events.  IMHO treating it as game state is also a simpler model.


> Why is what other players know an important part of your game state?
> The exact extents of other players' knowledge can't be public, so nobody
> but the server can really use this information, right?  What does the
> server do with it?  I guess it's useful for diplomacy, if you allow
> bartering information.

It can affect events and populations, and change how turns play out.  A
"player"'s knowledge represents roughly what an entire Faction knows, often
including population over which it rules.  For example knowledge of pending
invasion can spur population dispersal, unrest, etc.  Knowledge that a
foreign power is behind some act of espionage (whether true or untrue) can
affect public opinion and thus the practicality of war, etc.

Admitedly I'm not yet using this so it's just a nice potential, with the
main advantage IMHO being clearer code.


> > IMHO handling this can avoid the 3rd problem you mention.  I have
> > Players send Actions for a given Turn as the only API to change Game
> > State.  These actions don't take game object as args, but rather Ids as
> > you suggest below.
> 
> I'm now back to wanting to do that in both directions.  Except using
> strings instead of object IDs, since the parallel objects on the client 
> and server will be distinct.  (But they will use the same class, which
> is key to maintainability.  When you find yourself writing ClientGame
> and ServerGame, scream until the urge passes, then decouple the concept
> from its context until the need is gone.)

IMHO this seems overly complex.  I don't like maintaining parallel objects
or parallel calculation, as it smells like ClientGame/ServerGame...


> I want to make it symmetrical, so I want to build the client-side model
> objects using the action stream, rather than copying them directly.  It
> will be painfully boring, but I don't have *that* many actions, and
> they're fully documented from last time I decided to go this way.

Why?  I see no advantage to symmetry here.  IMHO the a trusted-server / 
untrusted-client asymetrical design is clearly called for.


> > IMHO it seems superflous to have the game update the information a
> > client sends, only to later access this same information for
> > calculations.  Why not just pass back the relavent Ids, and have the
> > Game API look up their True counterpart as needed?  This doesn't seem
> > onerous to me.
> 
> I wanted a way to bury this detail inside the framework, but it's not
> currently practical so I'm flipping completely the other way.  I'd forgotten
> how badly I want the ability to fast-forward and rewind through game history
> by applying and undoing actions.  (Lets the AI free-run while giving the
> ability to watch it as if it were moving more slowly.  Also lets you
> review other people's savegames to pick up strategy hints.)

I disagree that sending objects isn't practical, IMHO it just helps to
approach it slightly differently.  I'll try to explain what I mean by
roughly describing my game as an example;  if my design is crappy please feel
free to shoot holes in it! :-)

Much of my game's logic is based upon Copyable Actions which are used by
both Server and Client, e.g. MoveAction.  These have a .__call__(state)
which modifies the passed state as they see fit, as well as a
.validate(state) to ensure a move is legal.  Players create a list of such
Actions, and pass them to the Game server as their turn.  All the args for
Action are literals (such as Unit ids and movement direction), with Actions
knowing how to find the corresponding objects on passed in State.

With only literal args these Action objects can be passed safely to the
game, which will reconstitue it's own versions, validate them, then invoke
them by passing in it's True State.  Players are given their own PlayerState
view of TrueState each turn, with real (copies of) objects they can use for
display, UI and validation.

Basically it works like passing objects both ways, except it's safe.  The
costs are minor: referenceable objects must have Ids, Clients must create
Actions with 'object.id' instead of 'object', and Actions need to either
'object = state.getObject(objectId)' or use Game/State methods taking ids
for args.

IMHO Making all of this essentially part of the API is cleaner than having
bulk "Reconsitute Objects" methods.


Hope this is at least food for thought!  In any event I find talking about
my designs is a good way for me to step back and look at them from another
angle.

-Jasper





More information about the Twisted-Python mailing list