# HG changeset patch # User Simon Cross # Date 1258808108 0 # Node ID 8f012ef1f64f84a71291e47d44bb0941ecd5538f # Parent 9096c237928c2968a56ecf5d9350bf8ba05588bb Start of ability to serialize game state. diff -r 9096c237928c -r 8f012ef1f64f gamelib/animal.py --- a/gamelib/animal.py Sat Nov 21 12:48:12 2009 +0000 +++ b/gamelib/animal.py Sat Nov 21 12:55:08 2009 +0000 @@ -11,14 +11,23 @@ import sound import equipment import animations +import serializer -class Animal(Sprite): +class Animal(Sprite, serializer.Simplifiable): """Base class for animals""" STEALTH = 0 VISION_BONUS = 0 VISION_RANGE_PENALTY = 10 + SIMPLIFY = [ + 'pos', + 'equipment', + 'accoutrements', + 'abode', + 'facing', + ] + def __init__(self, image_left, image_right, tile_pos): # Create the animal somewhere far off screen Sprite.__init__(self, image_left, (-1000, -1000)) diff -r 9096c237928c -r 8f012ef1f64f gamelib/buildings.py --- a/gamelib/buildings.py Sat Nov 21 12:48:12 2009 +0000 +++ b/gamelib/buildings.py Sat Nov 21 12:55:08 2009 +0000 @@ -7,6 +7,7 @@ import imagecache import tiles import constants +import serializer # ignore os.popen3 warning generated by pygame.font.SysFont import warnings @@ -58,7 +59,7 @@ class BuildingFullError(Exception): pass -class Building(Sprite): +class Building(Sprite, serializer.Simplifiable): """Base class for buildings""" IS_BUILDING = True @@ -70,6 +71,18 @@ FLOORS = None HENHOUSE = False + SIMPLIFY = [ + 'pos', + 'size', + 'tile_no', + '_buy_price', + '_sell_price', + '_repair_price', + '_sun_on', + '_broken', + '_predators', + ] + def __init__(self, pos): """Initial image, tile vid position, size and tile number for building.""" self._set_images() diff -r 9096c237928c -r 8f012ef1f64f gamelib/equipment.py --- a/gamelib/equipment.py Sat Nov 21 12:48:12 2009 +0000 +++ b/gamelib/equipment.py Sat Nov 21 12:55:08 2009 +0000 @@ -4,13 +4,21 @@ import sound import imagecache import animations +import serializer -class Equipment(object): + +class Equipment(serializer.Simplifiable): IS_EQUIPMENT = True DRAW_LAYER = 0 UNDER_LIMB = False UNDER_EYE = False + SIMPLIFY = [ + '_buy_price', + '_sell_price', + '_name', + ] + def __init__(self): self._buy_price = self.BUY_PRICE self._sell_price = self.SELL_PRICE diff -r 9096c237928c -r 8f012ef1f64f gamelib/serializer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gamelib/serializer.py Sat Nov 21 12:55:08 2009 +0000 @@ -0,0 +1,107 @@ +""" +Interface for converting objects to and from simple structures: lists, dicts, +strings, integers and combinations there of. Used for sending objects over +the communications API. +""" + +REGISTERED_CLASSES = {} + +def simplify(item): + """Convert an item to a simple data structure.""" + if issubclass(type(item), Simplifiable): + return item.simplify() + elif type(item) is list: + return { 'list': [simplify(x) for x in item] } + elif type(item) is tuple: + return { 'tuple': tuple([simplify(x) for x in item]) } + elif item is None: + return { 'none': '' } + else: + return { 'raw': item } + +def unsimplify(value): + """Reverse the simplify process.""" + if value.has_key('class'): + cls = REGISTERED_CLASSES[value['class']] + return cls.unsimplify(value) + elif value.has_key('list'): + return [unsimplify(x) for x in value['list']] + elif value.has_key('tuple'): + return tuple([unsimplify(x) for x in value['tuple']]) + elif value.has_key('none'): + return None + elif value.has_key('raw'): + return value['raw'] + else: + raise SimplifyError("Unknown tar type key.") + +class SimplifyError(Exception): + pass + +class SimplifiableMetaclass(type): + def __init__(cls, name, bases, dct): + super(SimplifiableMetaclass, cls).__init__(name, bases, dct) + REGISTERED_CLASSES[cls.__name__] = cls + +class Simplifiable(object): + """ + Object which can be simplified() and unsimplified() (i.e. + converted to a data type which, for example, Python's XMLRPC + or json module is capable of handling). Each subclass + must provide SIMPLIFY (a list of strings giving the names + of attributes which should be stored) unless a parent class + provides the right thing. + """ + __metaclass__ = SimplifiableMetaclass + + # List of attributes which need to be stored and restored + SIMPLIFY = [] + + def make(cls): + """ + Create an object of this class without any attributes set. + """ + return cls.__new__(cls) + make = classmethod(make) + + def unsimplify(cls, value): + """ + Create an object of this class (or a sub-class) from its + simplification. + """ + actual_cls = REGISTERED_CLASSES[value['class']] + attrs = value['attributes'] + + if not issubclass(actual_cls, cls): + raise SimplifyError("Real class not a subclass of this class") + + if not len(attrs) == len(actual_cls.SIMPLIFY): + raise SimplifyError("Wrong number of attributes for this class") + + obj = actual_cls.make() + for attr, value in zip(actual_cls.SIMPLIFY, attrs): + setattr(obj, attr, unsimplify(value)) + + return obj + unsimplify = classmethod(unsimplify) + + def simplify(self): + """ + Create a simplified version (tar) of the object. + """ + value = {} + value['class'] = self.__class__.__name__ + + attrs = [] + for attr in self.SIMPLIFY: + o = getattr(self, attr) + attrs.append(simplify(o)) + + value['attributes'] = attrs + return value + + def copy(self): + """ + Return a copy of the object. + """ + return self.__class__.unsimplify(self.simplify())