changeset 415:8f012ef1f64f

Start of ability to serialize game state.
author Simon Cross <hodgestar@gmail.com>
date Sat, 21 Nov 2009 12:55:08 +0000
parents 9096c237928c
children c6dd21b75bf5
files gamelib/animal.py gamelib/buildings.py gamelib/equipment.py gamelib/serializer.py
diffstat 4 files changed, 140 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- 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))
--- 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()
--- 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
--- /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())