Skip to content

Instantly share code, notes, and snippets.

@jonschoning
Forked from mvanga/ecs-annotated.py
Created November 8, 2018 21:32
Show Gist options
  • Save jonschoning/b6750e0edbae0f4abf9857f8f5b67e22 to your computer and use it in GitHub Desktop.
Save jonschoning/b6750e0edbae0f4abf9857f8f5b67e22 to your computer and use it in GitHub Desktop.

Revisions

  1. @mvanga mvanga revised this gist Nov 8, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ecs-annotated.py
    Original file line number Diff line number Diff line change
    @@ -214,5 +214,5 @@ def update(self):

    @classmethod
    def update_all(cls):
    for system in systems:
    for system in cls.systems:
    system.update()
  2. @mvanga mvanga revised this gist Nov 6, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ecs-annotated.py
    Original file line number Diff line number Diff line change
    @@ -166,7 +166,7 @@ def get(cls, eid):
    # Note that there is also no restriction on which entities you access in
    # the update() function inside a System object. Above, I show an example
    # of picking entities having a single component ('movement'), but you can
    # instead easily choose to # pick out different sets, apply any kind of
    # instead easily choose to pick out different sets, apply any kind of
    # set operators, etc. before arriving at the entities you need and
    # continuing with the loop.
    #
  3. @mvanga mvanga revised this gist Nov 6, 2018. 2 changed files with 26 additions and 66 deletions.
    27 changes: 26 additions & 1 deletion ecs-annotated.py
    Original file line number Diff line number Diff line change
    @@ -163,11 +163,31 @@ def get(cls, eid):
    # runs some processing code, and finally, if needed, emits a new set of
    # events that get picked up in the next round by other systems.
    #
    # Note that there is also no restriction on which entities you access in
    # the update() function inside a System object. Above, I show an example
    # of picking entities having a single component ('movement'), but you can
    # instead easily choose to # pick out different sets, apply any kind of
    # set operators, etc. before arriving at the entities you need and
    # continuing with the loop.
    #
    # Here's a made-up example that illustrates how to do this using Python's
    # built-in set operations:
    #
    # def update(self):
    # ent_with_move = set(Entity.filter('movement'))
    # ent_with_pos = set(Entity.filter('position'))
    # ent_with_ai = set(Entity.filter('ai'))
    # entities = list((ent_with_move& ent_with_pos) | ent_with_ai)
    #
    # # Rest of the loop goes here and uses 'entities'
    #
    class System(object):
    systems = []
    subscriptions = {}

    def __init__(self):
    self.events = []
    self.systems.append(self)

    def subscribe(self, event_type):
    if event_type not in self.subscriptions:
    @@ -190,4 +210,9 @@ def inject(cls, event):
    subscriber.events.append(event)

    def update(self):
    pass
    pass

    @classmethod
    def update_all(cls):
    for system in systems:
    system.update()
    65 changes: 0 additions & 65 deletions ecs.py
    Original file line number Diff line number Diff line change
    @@ -1,65 +0,0 @@
    import uuid
    import json


    def Component(filename):
    with open(filename + '.json', 'r') as f:
    return json.load(f)


    class Entity(object):
    eindex = {} # Index mapping entity IDs to entity objects
    cindex = {} # Index mapping component names to entity objects

    def __init__(self):
    self.id = str(uuid.uuid4())
    self.components = []
    self.eindex[self.id] = self # Assumes ID's never collide

    def attach(self, component, namespace=None):
    self.components.append(component['type'])
    key = namespace if namespace else component['type']
    self.__dict__[key] = type('Component', (), component['schema'])()
    # Add to component index
    if component['type'] not in self.cindex:
    self.cindex[component['type']] = []
    self.cindex[component['type']].append(self)

    @classmethod
    def filter(cls, component):
    entities = cls.cindex.get(component)
    return entities if entities is not None else []

    @classmethod
    def get(cls, eid):
    return cls.eindex.get(eid)


    class System(object):
    subscriptions = {}

    def __init__(self):
    self.events = []

    def subscribe(self, event_type):
    if event_type not in self.subscriptions:
    self.subscriptions[event_type] = []
    self.subscriptions[event_type].append(self)

    def pending(self):
    # Get pending events and clear queue
    ret = self.events
    self.events = []
    return ret

    @classmethod
    def inject(cls, event):
    # All events must be dicts with a 'type' field
    event_type = event['type']
    if event_type not in cls.subscriptions:
    return
    for subscriber in cls.subscriptions[event_type]:
    subscriber.events.append(event)

    def update(self):
    pass
  4. @mvanga mvanga revised this gist Nov 5, 2018. 1 changed file with 17 additions and 11 deletions.
    28 changes: 17 additions & 11 deletions ecs-annotated.py
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,10 @@
    import uuid
    import json


    # Returns a python dictionary given a file containing a JSON-based
    # component definition. Every definition *must* contain a 'type'
    # and 'schema' fields inside a top-level dictionary. Here is an
    # and 'schema' field inside a top-level dictionary. Here is an
    # example of a simple schema file that defines a 'meta' component
    # containing a 'name' field.
    #
    @@ -14,8 +15,6 @@
    # "name": ""
    # }
    # }


    def Component(filename):
    with open(filename + '.json', 'r') as f:
    return json.load(f)
    @@ -32,16 +31,23 @@ def Component(filename):
    # sets a class attribute. Here is an example of how to attach
    # our 'meta' object from above to an entity:
    #
    # e = Entity()
    # e.attach(Component('meta')
    # e.meta.name = 'Player'
    # >>> e = Entity()
    # >>> e.__dict__
    # {'id': '5afec678-4c4e-44a9-be74-8764f62b61fd', 'components': []}
    # >>>
    # >>> e.attach(Component('meta'))
    # >>> pprint.pprint(e.__dict__)
    # {'components': ['meta'],
    # 'id': '5afec678-4c4e-44a9-be74-8764f62b61fd',
    # 'meta': <ecs.Component object at 0x108a1e400>}
    # >>> e.meta.name = 'Player'
    #
    # The attach() function also takes a namespace argument for
    # renaming longer component names when creating the attribute:
    #
    # e = Entity()
    # e.attach(Component('meta'), namespace='m')
    # e.m.name = 'Player'
    # >> e = Entity()
    # >> e.attach(Component('meta'), namespace='m')
    # >> e.m.name = 'Player'
    #
    # We also do some housekeeping: the 'components' object variable
    # keeps track of all components attached to this entity:
    @@ -109,6 +115,7 @@ def filter(cls, component):
    def get(cls, eid):
    return cls.eindex.get(eid)


    # The final class that completes our ECS implementation is the System
    # class for defining systems that update the world.
    #
    @@ -155,8 +162,7 @@ def get(cls, eid):
    # Note that above, the update() function first gets all pending events,
    # runs some processing code, and finally, if needed, emits a new set of
    # events that get picked up in the next round by other systems.


    #
    class System(object):
    subscriptions = {}

  5. @mvanga mvanga revised this gist Nov 5, 2018. 2 changed files with 187 additions and 122 deletions.
    187 changes: 187 additions & 0 deletions ecs-annotated.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,187 @@
    import uuid
    import json

    # Returns a python dictionary given a file containing a JSON-based
    # component definition. Every definition *must* contain a 'type'
    # and 'schema' fields inside a top-level dictionary. Here is an
    # example of a simple schema file that defines a 'meta' component
    # containing a 'name' field.
    #
    # // Contents of meta.json
    # {
    # "type": "meta",
    # "schema": {
    # "name": ""
    # }
    # }


    def Component(filename):
    with open(filename + '.json', 'r') as f:
    return json.load(f)


    # The Entity class defines a single entity in our system composed
    # of multiple components. At its heart, an Entity object is simply
    # an empty bucket associated with a unique ID.
    #
    # This class provides exactly one relevant function: a way to
    # attach a component dictionary to the entity. This function
    # simply takes the dictionary loaded using the Component()
    # function above, instantiates a new class on-the-fly, and
    # sets a class attribute. Here is an example of how to attach
    # our 'meta' object from above to an entity:
    #
    # e = Entity()
    # e.attach(Component('meta')
    # e.meta.name = 'Player'
    #
    # The attach() function also takes a namespace argument for
    # renaming longer component names when creating the attribute:
    #
    # e = Entity()
    # e.attach(Component('meta'), namespace='m')
    # e.m.name = 'Player'
    #
    # We also do some housekeeping: the 'components' object variable
    # keeps track of all components attached to this entity:
    #
    # >>> e = Entity()
    # >>> e.attach(Component('meta')
    # >>> e.components
    # ['meta']
    #
    # Note that namespacing maintains the original class name inside
    # the components array:
    #
    # >>> e = Entity()
    # >>> e.attach(Component('meta'), namespace='m')
    # >>> e.components
    # ['meta']
    #
    # Finally, we also track two reverse mappings: (i) to go from a
    # given entity ID to an entity object, and (ii) to go from a
    # component type to a list of entity objects. Both these are
    # implemented as class methods so no instantiated object is needed
    # to retrieve them.
    #
    # The first is useful in many cases where you want to reference a
    # particular entity and use it in a system. For example, an attack
    # component can simply store the ID of the entity being attacked.
    # The damage-calculation system simply looks up the entity using
    # this reverse index.
    #
    # target = Entity.get('47d78b7e-8c5c-417b-8f46-be0de7c0b62d')
    #
    # The second index is used in implementing systems that deal with
    # all entities having a particular component attached. For example,
    # a MovementSystem can easily grab all entities containing a
    # movement component:
    #
    # entities = Entity.filter('movement')
    #
    class Entity(object):
    eindex = {} # Index mapping entity IDs to entity objects
    cindex = {} # Index mapping component names to entity objects

    def __init__(self):
    self.id = str(uuid.uuid4())
    self.components = []
    self.eindex[self.id] = self # Assumes ID's never collide

    def attach(self, component, namespace=None):
    # Append component name to list of components
    self.components.append(component['type'])
    # Create a raw 'Component' object based on the JSON schema
    key = namespace if namespace else component['type']
    self.__dict__[key] = type('Component', (), component['schema'])()
    # Add to component index
    if component['type'] not in self.cindex:
    self.cindex[component['type']] = []
    self.cindex[component['type']].append(self)

    @classmethod
    def filter(cls, component):
    entities = cls.cindex.get(component)
    return entities if entities is not None else []

    @classmethod
    def get(cls, eid):
    return cls.eindex.get(eid)

    # The final class that completes our ECS implementation is the System
    # class for defining systems that update the world.
    #
    # The class is designed as a simple pub-sub system where each system
    # decides which game events it wants to be notified of. This is done
    # using the `subscribe()` method, which takes an event type (string)
    # as a parameter.
    #
    # Each System object contains its own list of pending events and
    # a class method, inject(), allows for injecting game events into the
    # entire set of systems. This function it basically looks up all
    # subscribers of that given event and simply appends the event into
    # each of their event lists. Note that at this point, the event has
    # not yet been handled, but simply registered as pending by appending
    # into each subscriber's events list.
    #
    # The reason the inject() function is a class method and not an object
    # level method is so that external parts of the game, such as the
    # input system, can freely inject events into systems.
    #
    # Finally, the update() function can be overridden by subclasses to
    # define their own custom game loop logic. The `pending()` function
    # can be used here to retrieve (and clear) all pending events in the
    # system, and the Entity.filter() function can be called to get a
    # filtered list of entities relevant to this system. Here is a very
    # simple example of how to implement a system:
    #
    # class MovementSystem(System):
    # def __init__(self):
    # super().__init__()
    # self.subscribe('move')
    #
    # def update(self):
    # # Get list of pending events and clear current event queue
    # events = self.pending()
    # # Filter entities by type. Fast because we use component_index.
    # entities = Entity.filter('movement')
    #
    # # Do something here that modifies state or generates events
    #
    # # Inject any new events at the end
    # self.inject({'type': 'move', 'data': randstr(10)})
    #
    # Note that above, the update() function first gets all pending events,
    # runs some processing code, and finally, if needed, emits a new set of
    # events that get picked up in the next round by other systems.


    class System(object):
    subscriptions = {}

    def __init__(self):
    self.events = []

    def subscribe(self, event_type):
    if event_type not in self.subscriptions:
    self.subscriptions[event_type] = []
    self.subscriptions[event_type].append(self)

    def pending(self):
    # Get pending events and clear queue
    ret = self.events
    self.events = []
    return ret

    @classmethod
    def inject(cls, event):
    # All events must be dicts with a 'type' field
    event_type = event['type']
    if event_type not in cls.subscriptions:
    return
    for subscriber in cls.subscriptions[event_type]:
    subscriber.events.append(event)

    def update(self):
    pass
    122 changes: 0 additions & 122 deletions ecs.py
    Original file line number Diff line number Diff line change
    @@ -1,85 +1,12 @@
    import uuid
    import json

    # Returns a python dictionary given a file containing a JSON-based
    # component definition. Every definition *must* contain a 'type'
    # and 'schema' fields inside a top-level dictionary. Here is an
    # example of a simple schema file that defines a 'meta' component
    # containing a 'name' field.
    #
    # // Contents of meta.json
    # {
    # "type": "meta",
    # "schema": {
    # "name": ""
    # }
    # }


    def Component(filename):
    with open(filename + '.json', 'r') as f:
    return json.load(f)


    # The Entity class defines a single entity in our system composed
    # of multiple components. At its heart, an Entity object is simply
    # an empty bucket associated with a unique ID.
    #
    # This class provides exactly one relevant function: a way to
    # attach a component dictionary to the entity. This function
    # simply takes the dictionary loaded using the Component()
    # function above, instantiates a new class on-the-fly, and
    # sets a class attribute. Here is an example of how to attach
    # our 'meta' object from above to an entity:
    #
    # e = Entity()
    # e.attach(Component('meta')
    # e.meta.name = 'Player'
    #
    # The attach() function also takes a namespace argument for
    # renaming longer component names when creating the attribute:
    #
    # e = Entity()
    # e.attach(Component('meta'), namespace='m')
    # e.m.name = 'Player'
    #
    # We also do some housekeeping: the 'components' object variable
    # keeps track of all components attached to this entity:
    #
    # >>> e = Entity()
    # >>> e.attach(Component('meta')
    # >>> e.components
    # ['meta']
    #
    # Note that namespacing maintains the original class name inside
    # the components array:
    #
    # >>> e = Entity()
    # >>> e.attach(Component('meta'), namespace='m')
    # >>> e.components
    # ['meta']
    #
    # Finally, we also track two reverse mappings: (i) to go from a
    # given entity ID to an entity object, and (ii) to go from a
    # component type to a list of entity objects. Both these are
    # implemented as class methods so no instantiated object is needed
    # to retrieve them.
    #
    # The first is useful in many cases where you want to reference a
    # particular entity and use it in a system. For example, an attack
    # component can simply store the ID of the entity being attacked.
    # The damage-calculation system simply looks up the entity using
    # this reverse index.
    #
    # target = Entity.get('47d78b7e-8c5c-417b-8f46-be0de7c0b62d')
    #
    # The second index is used in implementing systems that deal with
    # all entities having a particular component attached. For example,
    # a MovementSystem can easily grab all entities containing a
    # movement component:
    #
    # entities = Entity.filter('movement')
    #
    class Entity(object):
    eindex = {} # Index mapping entity IDs to entity objects
    cindex = {} # Index mapping component names to entity objects
    @@ -90,9 +17,7 @@ def __init__(self):
    self.eindex[self.id] = self # Assumes ID's never collide

    def attach(self, component, namespace=None):
    # Append component name to list of components
    self.components.append(component['type'])
    # Create a raw 'Component' object based on the JSON schema
    key = namespace if namespace else component['type']
    self.__dict__[key] = type('Component', (), component['schema'])()
    # Add to component index
    @@ -109,53 +34,6 @@ def filter(cls, component):
    def get(cls, eid):
    return cls.eindex.get(eid)

    # The final class that completes our ECS implementation is the System
    # class for defining systems that update the world.
    #
    # The class is designed as a simple pub-sub system where each system
    # decides which game events it wants to be notified of. This is done
    # using the `subscribe()` method, which takes an event type (string)
    # as a parameter.
    #
    # Each System object contains its own list of pending events and
    # a class method, inject(), allows for injecting game events into the
    # entire set of systems. This function it basically looks up all
    # subscribers of that given event and simply appends the event into
    # each of their event lists. Note that at this point, the event has
    # not yet been handled, but simply registered as pending by appending
    # into each subscriber's events list.
    #
    # The reason the inject() function is a class method and not an object
    # level method is so that external parts of the game, such as the
    # input system, can freely inject events into systems.
    #
    # Finally, the update() function can be overridden by subclasses to
    # define their own custom game loop logic. The `pending()` function
    # can be used here to retrieve (and clear) all pending events in the
    # system, and the Entity.filter() function can be called to get a
    # filtered list of entities relevant to this system. Here is a very
    # simple example of how to implement a system:
    #
    # class MovementSystem(System):
    # def __init__(self):
    # super().__init__()
    # self.subscribe('move')
    #
    # def update(self):
    # # Get list of pending events and clear current event queue
    # events = self.pending()
    # # Filter entities by type. Fast because we use component_index.
    # entities = Entity.filter('movement')
    #
    # # Do something here that modifies state or generates events
    #
    # # Inject any new events at the end
    # self.inject({'type': 'move', 'data': randstr(10)})
    #
    # Note that above, the update() function first gets all pending events,
    # runs some processing code, and finally, if needed, emits a new set of
    # events that get picked up in the next round by other systems.


    class System(object):
    subscriptions = {}
  6. @mvanga mvanga revised this gist Nov 5, 2018. 1 changed file with 7 additions and 4 deletions.
    11 changes: 7 additions & 4 deletions ecs.py
    Original file line number Diff line number Diff line change
    @@ -14,11 +14,13 @@
    # "name": ""
    # }
    # }
    #


    def Component(filename):
    with open(filename + '.json', 'r') as f:
    return json.load(f)


    # The Entity class defines a single entity in our system composed
    # of multiple components. At its heart, an Entity object is simply
    # an empty bucket associated with a unique ID.
    @@ -42,7 +44,7 @@ def Component(filename):
    # e.m.name = 'Player'
    #
    # We also do some housekeeping: the 'components' object variable
    # keeps track of all components attached to this
    # keeps track of all components attached to this entity:
    #
    # >>> e = Entity()
    # >>> e.attach(Component('meta')
    @@ -138,7 +140,7 @@ def get(cls, eid):
    # def __init__(self):
    # super().__init__()
    # self.subscribe('move')
    #
    #
    # def update(self):
    # # Get list of pending events and clear current event queue
    # events = self.pending()
    @@ -153,7 +155,8 @@ def get(cls, eid):
    # Note that above, the update() function first gets all pending events,
    # runs some processing code, and finally, if needed, emits a new set of
    # events that get picked up in the next round by other systems.
    #


    class System(object):
    subscriptions = {}

  7. @mvanga mvanga revised this gist Nov 5, 2018. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions meta.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    {
    "type": "meta",
    "schema": {
    "name": ""
    }
    }
  8. @mvanga mvanga created this gist Nov 5, 2018.
    184 changes: 184 additions & 0 deletions ecs.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,184 @@
    import uuid
    import json

    # Returns a python dictionary given a file containing a JSON-based
    # component definition. Every definition *must* contain a 'type'
    # and 'schema' fields inside a top-level dictionary. Here is an
    # example of a simple schema file that defines a 'meta' component
    # containing a 'name' field.
    #
    # // Contents of meta.json
    # {
    # "type": "meta",
    # "schema": {
    # "name": ""
    # }
    # }
    #
    def Component(filename):
    with open(filename + '.json', 'r') as f:
    return json.load(f)

    # The Entity class defines a single entity in our system composed
    # of multiple components. At its heart, an Entity object is simply
    # an empty bucket associated with a unique ID.
    #
    # This class provides exactly one relevant function: a way to
    # attach a component dictionary to the entity. This function
    # simply takes the dictionary loaded using the Component()
    # function above, instantiates a new class on-the-fly, and
    # sets a class attribute. Here is an example of how to attach
    # our 'meta' object from above to an entity:
    #
    # e = Entity()
    # e.attach(Component('meta')
    # e.meta.name = 'Player'
    #
    # The attach() function also takes a namespace argument for
    # renaming longer component names when creating the attribute:
    #
    # e = Entity()
    # e.attach(Component('meta'), namespace='m')
    # e.m.name = 'Player'
    #
    # We also do some housekeeping: the 'components' object variable
    # keeps track of all components attached to this
    #
    # >>> e = Entity()
    # >>> e.attach(Component('meta')
    # >>> e.components
    # ['meta']
    #
    # Note that namespacing maintains the original class name inside
    # the components array:
    #
    # >>> e = Entity()
    # >>> e.attach(Component('meta'), namespace='m')
    # >>> e.components
    # ['meta']
    #
    # Finally, we also track two reverse mappings: (i) to go from a
    # given entity ID to an entity object, and (ii) to go from a
    # component type to a list of entity objects. Both these are
    # implemented as class methods so no instantiated object is needed
    # to retrieve them.
    #
    # The first is useful in many cases where you want to reference a
    # particular entity and use it in a system. For example, an attack
    # component can simply store the ID of the entity being attacked.
    # The damage-calculation system simply looks up the entity using
    # this reverse index.
    #
    # target = Entity.get('47d78b7e-8c5c-417b-8f46-be0de7c0b62d')
    #
    # The second index is used in implementing systems that deal with
    # all entities having a particular component attached. For example,
    # a MovementSystem can easily grab all entities containing a
    # movement component:
    #
    # entities = Entity.filter('movement')
    #
    class Entity(object):
    eindex = {} # Index mapping entity IDs to entity objects
    cindex = {} # Index mapping component names to entity objects

    def __init__(self):
    self.id = str(uuid.uuid4())
    self.components = []
    self.eindex[self.id] = self # Assumes ID's never collide

    def attach(self, component, namespace=None):
    # Append component name to list of components
    self.components.append(component['type'])
    # Create a raw 'Component' object based on the JSON schema
    key = namespace if namespace else component['type']
    self.__dict__[key] = type('Component', (), component['schema'])()
    # Add to component index
    if component['type'] not in self.cindex:
    self.cindex[component['type']] = []
    self.cindex[component['type']].append(self)

    @classmethod
    def filter(cls, component):
    entities = cls.cindex.get(component)
    return entities if entities is not None else []

    @classmethod
    def get(cls, eid):
    return cls.eindex.get(eid)

    # The final class that completes our ECS implementation is the System
    # class for defining systems that update the world.
    #
    # The class is designed as a simple pub-sub system where each system
    # decides which game events it wants to be notified of. This is done
    # using the `subscribe()` method, which takes an event type (string)
    # as a parameter.
    #
    # Each System object contains its own list of pending events and
    # a class method, inject(), allows for injecting game events into the
    # entire set of systems. This function it basically looks up all
    # subscribers of that given event and simply appends the event into
    # each of their event lists. Note that at this point, the event has
    # not yet been handled, but simply registered as pending by appending
    # into each subscriber's events list.
    #
    # The reason the inject() function is a class method and not an object
    # level method is so that external parts of the game, such as the
    # input system, can freely inject events into systems.
    #
    # Finally, the update() function can be overridden by subclasses to
    # define their own custom game loop logic. The `pending()` function
    # can be used here to retrieve (and clear) all pending events in the
    # system, and the Entity.filter() function can be called to get a
    # filtered list of entities relevant to this system. Here is a very
    # simple example of how to implement a system:
    #
    # class MovementSystem(System):
    # def __init__(self):
    # super().__init__()
    # self.subscribe('move')
    #
    # def update(self):
    # # Get list of pending events and clear current event queue
    # events = self.pending()
    # # Filter entities by type. Fast because we use component_index.
    # entities = Entity.filter('movement')
    #
    # # Do something here that modifies state or generates events
    #
    # # Inject any new events at the end
    # self.inject({'type': 'move', 'data': randstr(10)})
    #
    # Note that above, the update() function first gets all pending events,
    # runs some processing code, and finally, if needed, emits a new set of
    # events that get picked up in the next round by other systems.
    #
    class System(object):
    subscriptions = {}

    def __init__(self):
    self.events = []

    def subscribe(self, event_type):
    if event_type not in self.subscriptions:
    self.subscriptions[event_type] = []
    self.subscriptions[event_type].append(self)

    def pending(self):
    # Get pending events and clear queue
    ret = self.events
    self.events = []
    return ret

    @classmethod
    def inject(cls, event):
    # All events must be dicts with a 'type' field
    event_type = event['type']
    if event_type not in cls.subscriptions:
    return
    for subscriber in cls.subscriptions[event_type]:
    subscriber.events.append(event)

    def update(self):
    pass