EventBus

Hey guys me again,
I have been having some issues with maintaining my slick2D game in regards to notifying other areas of code something has happened. My solution was to create EventBus essentially a very simple version of the GWT version (I work with it at work so to me its second nature).

The code looks as follows:


package uk.co.digitaldragonstudios.engine.events;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EventBus {
	private static EventBus eventBus;
	private Map<EventType,List<Listener<Event>>> bus =
		new HashMap<EventType,List<Listener<Event>>>();
	
	public static EventBus get() {
		if(eventBus == null) {
			eventBus = new EventBus();
		}
		return eventBus;
	}
	
	public void addListener(EventType type,Listener<Event> event) {
		if(bus.get(type) != null) {
			bus.get(type).add(event);
		}
		else {
			List<Listener<Event>> listeners = new ArrayList<Listener<Event>>();
			listeners.add(event);
			
			bus.put(type, listeners);
		}
	}
	
	public boolean fireEvent(EventType type,Event event) {
		if(bus.get(type) != null) {
			List<Listener<Event>> listeners = bus.get(type);
			for(Listener<Event> listener : listeners) {
				listener.onEvent(event);
				
				if(event.isCancelled()) {
					return false;
				}
			}
			return true;
		}
		return false;
	}
}

My questions are, is there a better way to do it than this and should it be done in games?

i understand it would never work for Java 4k of course :wink:

Your current implementation makes it impossible to add a listener of type X while you are handling an event of type X, due to the resulting ConcurrentModificationException.

Yes true but in most cases you would register an event long before your actually gonna fire any… since object creation need be avoided in the update loop.

p.s Thanks for the quick reply as always riven :slight_smile:

EDIT:
although thinking about it firing an event involves creating one in the first place, hmm.

Also does it mean it worth doing it this way (after i solve the concurrency etc out)?

So I have been looking at this and one way in which it could be done was to use a “ConcurrentSkipListSet” instead if a list which if I understand your point Riven would be solved as there is no longer a list that can throw the problem. Another idea I found was using annotations to signify a event handler this removes the need for Anonymous Inner Classes which apparently the GC and has caused memory leaks and so long as I ensure i keep track of the method and not keep looking up shouldn’t slow anything down.

Also both the set and use of reflection with annotations is supported in Andriod and actually suggested.

You need the ability to remove a listener (during the event, too).

You could run all the event handling code on the AWT event thread.
e.g.

public void addListener(final EventType type,final Listener<Event> event) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            if(bus.get(type) != null) {
              bus.get(type).add(event);
            }
            else {
              List<Listener<Event>> listeners = new ArrayList<Listener<Event>>();
              listeners.add(event);
             
              bus.put(type, listeners);
            }
        }
    }
}

You do the same with fireEvent and removeListener. You could also make all methods synchronized but that could result in a deadlock if you try to add an event inside an event listener.