Giter Club home page Giter Club logo

Comments (15)

benmoran56 avatar benmoran56 commented on August 12, 2024 5

Thanks for opening the ticket. I wasn't planning on adding Event Dispatching to esper. Mostly because other libraries exist, and are easy enough to mix in. For example the pyglet EventDispatcher framework, which I use myself.

That said, this is not the first request for this that I've received. I'll have to think about this for a little bit.

from esper.

benmoran56 avatar benmoran56 commented on August 12, 2024 3

It wouldn't be too hard to add a light-weight event dispatching system to esper. I'm not sure how far I would want to take this though. Consider the following simple implimentation:

Event handlers could be registered like this:

self.world.set_handler('on_pickup', self.my_method)
self.world.set_hanlder('on_pickup', my_func)

Events can be dispatched by name, and from anywhere that has access to the World:

self.world.dispatch_event('on_pickup', arg1, etc)

Events would be unregistered like this:

self.world.remove_hanlder('on_pickup', my_func)

from esper.

benmoran56 avatar benmoran56 commented on August 12, 2024 3

I mocked up a quick prototype of this. The API is essentially the same as what I posted above. If you'd like to have a look, it's in the eventsystem branch:

https://github.com/benmoran56/esper/tree/eventsystem

from esper.

benmoran56 avatar benmoran56 commented on August 12, 2024 1

I just pushed out a new Esper release, with an event system. To keep it simple, it's direct dispatch. This is similar to event systems in libraries like pyglet.

For lazily handling events, you can easily cache them for later iteration:

class MoveProcessor(Processor):
    def __init__(self):
        self.event_queue = []
        esper.set_handler('on_move', self.move_handler)

    def move_handler(self, *args):
        self.event_queue.append(args)

    def process(self, *args, **kwargs):
        for args in self.event_queue:
            ...
            do_something_with_event()
            ...
        self.event_queue.clear()

Let me know if you have any questions about how to make it work.

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

Thanks for your answer. If I found time to do this, I would come back with a proposition on this.

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

Looks nice and simple.

don't you think the event processing should be inside the processor? It bugs me that data/components may be read and modified in an event processing rather than in a processor.

from esper.

benmoran56 avatar benmoran56 commented on August 12, 2024

(Just a heads-up, but that code I posted earlier is probably non-functional. Maybe it would be better to do esper.set_handler(), etc ?)

What do you think that might look like? Could you share some pseudocode maybe?

EDIT: nevermind, you already did in your first post :)

If Events are iterated over, instead of being dispatched directly, then I think some kind of internal event caching would be necessary. For example:

ProcessorA
ProcessorB.receive('on_coin') --> None
ProcessorC
ProcessorD.dispatch('on_coin', 3)
ProcessorE
ProcessorF.receive('on_coin') --> 3

If ProcessorD dispatches an event, it would have to be stored internally so that ProcessorB has a chance to receive it.
What's not clear to me is how long is this cached for? I haven't used any event systems that work this way myself.

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

Actually, I have a working prototype on my side. I will push it there and we can discuss it.

from esper.

Lehnart avatar Lehnart commented on August 12, 2024
#Somewhere in esper.py


class Event:
    """ Not sure it is useful to have an Event class but let s go with it ... """
    def __init__(self):
        pass

    def key(self) -> Type:
        return self.__class__


class EventQueue:

    def __init__(self):
    """ Event queue is a dictionary. Keys are event class and values are list of event instances and their living duration as an integer """
        self._queue = {}

    def add(self, key: Type, event: object):
    """ Add a new event of a given type inside the queue """
        if key not in self._queue:
            self._queue[key] = []
        self._queue[key].append([event, 0])

    def tick(self):
    """ Increment lifetime of events """
        for key in self._queue.keys():
            for event in self._queue[key]:
                event[1] += 1

        for key in self._queue.keys():
            self._queue[key] = [msg for msg in self._queue[key] if msg[1] < 2]

    def get(self, key: Type) -> List:
    """ get all events of a given type if they have been their for exactly one loop of process """
        if key not in self._queue:
            return []
        return [event[0] for event in self._queue[key] if event[1] == 1]

EventQueue is the class responsible for the dispatch of the events. Event have a kind of life time :

  • When they are created their lifetime is 0. They can't be read or processed by a processor.
  • At the next esper.World process loop, their lifetime is 1 and they are ready to be received by the processors.
  • Next esper.World process loop, their lifetime is 2 and they are destroyed.

This way, each event can be read only once by a processor (assuming that each processor run during a world process loop).

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

World Class :

class World:
    # ...
    def publish(self, event: Event):
        """ When someone want to publish a new event """
        self._message_queue.add(event.key(), event)

    def receive(self, event_class) -> List:
        """ When  a processor want to access given type of events """
        return self._message_queue.get(event_class)
    # ...

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

An example in a processor :

class MoveProcessor(Processor):

    def process(self, *args, **kwargs):
        move_events = self.world.receive(MoveEvent)
        for move_event in move_events:
            r = self.world.component_for_entity(move_event.ent, RectComponent)
            r.move(move_event.dx, move_event.dy)

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

Let me know what you think of this.

2 more comments on this:

  • What matters for me is really to decouple processors. So I don't want my application to crash if a given event is not sent or if an event is sent but no one is receiving it. Just keeping the event for one process loop iteration helps for that.
  • The caching can't be easier I guess. I assume we don't require a real-time event system. It should be ok to wait for a process loop iteration to receive an event. Maybe we can do something fancier with processors subscribing to the event queue but I didn't need it.

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

Perfectly fine with this. Thanks a lot for your work!

from esper.

benmoran56 avatar benmoran56 commented on August 12, 2024

No problem, and I hope it's useful.
By the way, if you didn't see it, there is a short explanation at the bottom of the README.

from esper.

Lehnart avatar Lehnart commented on August 12, 2024

Yep. I did read that and it was crystal clear. I ll test it soon in my own projects.

from esper.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.