junkdog / artemis-odb

A continuation of the popular Artemis ECS framework
BSD 2-Clause "Simplified" License
779 stars 112 forks source link

[feature request] Inter-system communication layer #17

Closed apotapov closed 10 years ago

apotapov commented 11 years ago

Add a communication layer to EntitySystem either through event or message passing. Perhaps something like Guava's EventBus (https://code.google.com/p/guava-libraries/wiki/EventBusExplained)

The idea is to decouple systems and improve inter-system communication.

junkdog commented 11 years ago

I don't think this would be a good match for Artemis core: planning on creating a few sub-projects for Artemis, with more specific extensions. Granted, most games would require some event system, but there's nothing stopping you from tacking it on yourself. I prefer exploiting libgdx's event system, so that I can more easily intercept UI events and game events with the same approach - looks something like EventSystem, a CommandEvent and a CommandEventListener.

Also, maybe I'm misunderstanding, but why is (reasonable) coupling between systems a bad thing? With reasonable coupling, I mean something like this. Wouldn't events just be lost, potentially leading to presumed bugs that are nothing more than systems/managers that forgot to register themselves?

apotapov commented 11 years ago

Coupling might not be a bad thing on small scale, but is a huge pain to manage once the number of systems and interconnections. Event or message based communication is a very common pattern and allows to add new functionality easily, similar to the whole Entity System paradigm. Just read the wiki page on the pattern: http://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern and they bring up a good point that the coupling instead of being explicit becomes semantic through the event/message types that get passed around. Interesting thought.

There are a lot of patterns around decoupling components of a system: Observer/Observable, Listener, Pub/Sub, etc. There's no silver bullet, and you are right, not sure if it makes sense to add anything to the core Artemis.

As for EventSystem in hallucinolog, it's a good approach if the number of different event types is small. This approach will not be scalable if the number of different types of event would grow and if receivers only care about a small subset of events. The number of send methods will grow tremendously and cycles will be wasted sending events to receivers that won't care about them.

junkdog commented 11 years ago

We could maybe create an artemis-event module then? I've experimented with mbassador before, but wasn't too fond of the API (my excuse for stopping work on the project/experiment) - seems pretty good though.

As for EventSystem in hallucinolog, it's a good approach if the number of different event types is small. This approach will not be scalable if the number of different types of event would grow and if receivers only care about a small subset of events. The number of send methods will grow tremendously and cycles will be wasted sending events to receivers that won't care about them.

Yeah, most of my events are interaction-based (user input, ui events etc), not meant for big throughput.

gjroelofs commented 11 years ago

My main points in choosing for an event-based design were:

Downsides specific to Events

Guava The eventbus is just a small section, easily implemented ourselves. Its two main features are flatting the class hierarchy of the Event to ensure superclass specifications also receive the call and dynamic dispatch to the correct method using a simple annotation lookup and reflection call.

However, it has several downsides in an game development environment:

gjroelofs commented 11 years ago

@junkdog I agree with you that event-based architecture isn't needed, nor wanted in every situation. Maybe one approach could be that the current EntitySystems are all a wrapper around an event-based system that masks the implementation, and by default accepts the dT events. The default implementation could then also just register these systems under the delta time event. This would maintain the API, without polluting World with special cases for non-event based systems.

If more granularity is required, the base class can be used, and registered under different types of events.

apotapov commented 11 years ago

I think I figured out a pretty nice approach to an event-based architecture for Artemis. If you are interested, take a look at the commit here:

https://github.com/apotapov/gdx-artemis/commit/babe07bfed141dc00582b75fc0ccb70ae0ed2a99

I've tested out the BasicEventSystem, and it works pretty well in the game that I'm developing.

The idea is as follows (see BasicEventSystemTest)

The events are queued inside the EventSystem, so the systems that recieve events have to actively poll the world to get the events they are interested in. This approach allows to have a single entry point into each system (processEntities()) and avoids systems having to do their own event caching.

It also allows the user to implement their own event systems that queue and deliver events differently. (see FastDeliveryEventSystem and DedicatedQueueEventSystem)

The user is not required to use the event systems for inter-system communication if they don't want to, but this allows them to do so pretty easily.

Here's what I currently use events for in my game:

junkdog commented 11 years ago

@apotapov sorry, haven't had a chance to look at it in detail yet - was planning to do it during the weekend, but was bogged down with work and a cold. I'll take a look at it as soon as things clear up.

Namek commented 11 years ago

I also think that event systems should not be included in Artemis.

@apotapov For me such event system is not enough. I don't like to diverge with same things in many ways. What I wanted to explain is that not everyting in my framework is done in Artemis (Entity System in general) but some things (even game UI) are done out of artemis. So event system working only in Artemis is not enough for me, because I want the same system working outside of Artemis.

What I'm currently trying to achieve is to add some pooling into MBassador message library (currently discussing lack of pooling bennidi/mbassador#54) which I use in a global way:

public abstract class Event {
}

public final class Messages {
    private static class Singleton {
        private static final MBassador<Event> eventBus = new MBassador<Event>(BusConfiguration.Default(1));
    }

    public static MBassador<Event> getEventBus() {
        return Singleton.eventBus;
    }

    public static void subscribe(Object listener) {
        getEventBus().subscribe(listener);
    }

    public static void unsubscribe(Object listener) {
        getEventBus().unsubscribe(listener);
    }

    public static <T> T createEvent(Class<T> type) {
        return Pools.obtain(type);
    }

    public static <T extends Event> void publishEvent(T event) {
        getEventBus().publish(event);
        Pools.free(event);
    }

    public static void publishEventAsync(Event event) {
        MessagePublication publication = getEventBus().publishAsync(event);

        // TODO asynchronous event need to be freed little later
        //publication.setFinishEvent(true);
    }
}

It's a nice library because it doesn't limit one to only use it as event system but also passing messages (any objects) across game/application. The only thing is lack of possibility to do pooling in asynchronous events (and also library itself isn't pooled, for example publishAsync() creates MessagePublication object).

Gornova commented 11 years ago

Hi, my 2 cents on this. I'd like to see an entity system on top of artemis, generic enough and inspired to guava. I've found interesting @apotapov implementation here: https://github.com/apotapov/gdx-artemis/commit/babe07bfed141dc00582b75fc0ccb70ae0ed2a99 @apotapov it deserves a library, to deploy near artemis, what do you think ?

Bonus: a friend of mine build this library http://leibnizwheel.wordpress.com/2013/05/02/cat-js-building-large-scale-web-applications/ and discuss some ideas to get a web application working with it. I hope you can find helpful for this kind of work!

See ya

apotapov commented 11 years ago

@Namek I understand the need for a messaging system outside of Artemis. I got into a situation in my game where I needed a non-entity system to send messages, which turned out to be pretty easy actually. (In my case)

The only thing I would recommend if you decide to go that route is to create a polling message system or at least have an option to do so rather than a publishing one. The reason for that is, you only want your Entity Systems to do work in the process() function call. If you have a second point of entry into the entity system when a message gets pushed to it, you either have to store the message for later processing or somehow pull the relative entities and apply the message to them. Messy either way.

If instead you can just request latest messages during the process() call. Things become much cleaner. You do have to be careful there and make sure that messages get cached and delivered correctly in the messaging system, but it shouldn't be too difficult.

@Gornova thanks for the vote of confidence. After examining a few different approaches to message passing I decided that the implementation I added to gdx-artemis makes the most sense. Here are my reasons:

  1. Avoids the use of reflection used in Guava as its expensive at run time and not fully supported by GWT.
  2. Built into Artemis directly but is non-intrusive so that the library users don't have to bother with it if they don't want to.
  3. Events are polled and not pushed. The advantage explained above.
  4. Also, I believe that this could be coupled with an external messaging/event system. Where you have a certain interface that just relays events from one event system to another.

In any case, I don't doubt that there are better ways to implement a messaging system than what I currently have in gdx-artemis. However, I haven't seen any that would be a better fit for my purposes at least.

Namek commented 11 years ago

@apotapov You are totally right about polling events in systems. But the thing is (which actually I wrote before about) that I do not want to receive events only in Artemis systems but other places too. That's why i'd like to have some general-out-of-Artemis event/message system.

apotapov commented 11 years ago

Yeah, that makes sense. If you can come up with something generic, I'd love to take a look.

junkdog commented 10 years ago

I'm personally not too keen on expanding artemis with its own event system or pulling in external dependencies in the core library (I think it's an asset that the base library is plug-n-play, less hassle for the less experienced), but maybe one solution/workaround would be to provide two separate builds of Artemis? One with the World class and one without - where you provide your own world implementation under the com.artemis package.

We could create sub-modules for different world implementations.

apotapov commented 10 years ago

Up to you. At this point I think I'll stick with my implementation of Artemis. It works for my purposes. Feel free to borrow anything you think is appropriate from that project and if you don't mind I will do the same.

junkdog commented 10 years ago

Sure thing!

I'll revisit the many worlds option when I get around to implementing a separation between simulation and graphics systems (so that they each can run on a different time-step). Probably won't get around to it before sometime after new year's though.