Skip to content

Instantly share code, notes, and snippets.

@abueide
Created September 2, 2020 07:39
Show Gist options
  • Save abueide/12999bae4f7ed33047a69d2429702b5c to your computer and use it in GitHub Desktop.
Save abueide/12999bae4f7ed33047a69d2429702b5c to your computer and use it in GitHub Desktop.
pubsub system
package com.abysl.gdxcrawler.ecs.events
import com.abysl.gdxcrawler.ecs.events.input.MoveEvent
import java.util.*
class EventManager {
// has to be public because of inline fun, would rather be private
// Have to do a MutableList<Any>, then do unsafe cast later. Couldn't think of another way to do it
val subscriptions: MutableMap<Class<out Event>, MutableList<Any>> = mutableMapOf()
private val events: Queue<Event> = LinkedList()
// don't really understand inline and reified, intellij just told me I need it, otherwise error
inline fun <reified T: Event> subscribe(noinline subscriber: (T) -> Unit) {
val event = T::class.java
if(subscriptions[event] == null){
subscriptions[event] = mutableListOf()
}
subscriptions[event]!!.add(subscriber)
}
fun publish(event: Event){
events.add(event)
}
private fun <T: Event> processEvent(event: T){
subscriptions[event.javaClass]?.forEach {
(it as ((T) -> Unit)).invoke(event)
}
}
// Clear out the event queue
fun process(){
while(events.size > 0){
processEvent(events.poll())
}
}
}
// ------------------------------------------ EXAMPLE USAGE -----------------------------------------------
data class MoveEvent(val entityId: Int, val direction: Vector2) : Event()
val manager = EventManager()
manager.subscribe<MoveEvent>{
println("${it.entityId}, ${it.direction}")
}
manager.publish(MoveEvent(0, Vector2(0f, 0f));
manager.process()
output:
0, (0.0,0.0)
@SerVB
Copy link

SerVB commented Sep 2, 2020

I see the following improvements:

        if(subscriptions[event] == null){
            subscriptions[event] = mutableListOf()
        }
        subscriptions[event]!!.add(subscriber)

to

        subscriptions.getOrPut(event) { mutableListOf() }.add(subscriber)
  1. Change type like this: subscriptions: MutableMap<Class<out Event>, MutableList<(Event) -> Unit>> (instead of subscriptions: MutableMap<Class<out Event>, MutableList<Any>>).

After it, you are able to change

    private fun <T: Event> processEvent(event: T){
        subscriptions[event.javaClass]?.forEach {
            (it as ((T) -> Unit)).invoke(event)
        }
    }

to

    private fun <T: Event> processEvent(event: T){
        subscriptions[event.javaClass]?.forEach {
            it(event)
        }
    }
  1. Maybe some reformatting :)

@abueide
Copy link
Author

abueide commented Sep 2, 2020

Thanks, for the first suggestion, I didn't know that existed, much nicer.
The problem with the second suggestion is that it breaks the subscribe function and forces me to also change the subscribe parameter from (T) -> Unit to (Event) -> Unit, which is bad because then every time you use the subscribe function you have to cast the parameter thats being passed.

For example the move event example becomes this

eventmanager.subscribe<MoveEvent>{
    val move = it as MoveEvent
    println("${move.entityId}, ${move.direction}")
}

@SerVB
Copy link

SerVB commented Sep 2, 2020

No problem.

  1. Oh, didn't think of it... OK

@cypherdare
Copy link

val subscriptions can be marked @PublishedApi internal to reduce its visibility. I don't think the function type casting can be avoided.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment