RxBukkit - A new event philosphy

Discussion in 'Spigot Plugin Development' started by Twister915, Jan 12, 2016.

  1. This is now an inbuilt feature in my new library, Redemptive
    https://github.com/Twister915/redemptive
    After using this pattern for the last few months, I've really found myself stunned at how much this simplifies. Check out, for example, the prompting system in commands. So few lines for such a cool feature!


    Today I wanted to demonstrate a new method for handling events entirely. I present- RxJavaBukkit- which is more of an idea than an implementation.

    I encountered RxJava a few months ago and started toying around with it. It was quite an interesting system- providing me with a whole new perspective when it came to the way I modeled my programs. Have a look for yourself if you want the detailed, but I'll give you a short (and graphical) rundown of what the philosophy is and how it relates to Bukkit.

    Below is a diagram explaining what an "observable" is. Imagine, for a moment, a "stream" of events of a particular type. This stream of events are "emitted" at certain times in a certain order. Time can be represented as a line, and the events can be represented as items on the line.

    [​IMG]

    This philosophy can be applied to Bukkit. We can make Bukkit's events emit themselves this way. In a way, this is already how it works, but the semantics are where the real improvement is needed.

    The major problem with Bukkit's listener system is the semantic design. Reflectively calling annotated methods is, in practice, easy for both the user and the programmer. However, you lack the control that an observable stream could provide. Allow me to demonstrate some of the powerful semantic features you have with this method of event observation:

    First, let's start with a basic PlayerJoinEvent listener, which should send Twister a message when he joins.

    [​IMG]

    Notice a few key advantages over Bukkit's inbuilt event handling system. You can filter, map, and do other operations who's origins are in functional programming. If you use the Java 8 stream system, you'll be familiar with this.

    Also notice, importantly, that when you subscribe to an observable, you are handed a subscription object, which can be used to unsubscribe from an observable, and thus unsubscribe your listener (if your implementation does this properly).

    Next, let's have a look at a few of the even more powerful operators- those that operate with time in mind. (notice also this is written without the use of lambdas so that this plugin could run on Java 7)

    [​IMG]

    Notice the "sample" operator. This operator takes the first event that occurs, then does not send any more until 500ms has passed, at which time it will emit the next and repeat this process. This is documented well by rxjava, so I'll link you there: http://reactivex.io/documentation/operators/sample.html

    Now let's spice it up a bit:

    I need to create a feature where users are not allowed to fly if they:
    * "can no longer fly" after moving. This typically means they walk out of faction territory.
    * Engage in PvP at any time while flying.


    Here's the RxJava implementation of this task:
    [​IMG]

    Notice a few key operators:
    * Sample, from before
    * merge, which merges two observables together.
    * takeWhile- an operator which checks a condition, and if it's found to be false, causes the subscription to automatically unsubscribe (which unregisters the listener from Bukkit)
    * take(1)- an operator which allows you to specify how many events you want sent to you. In this case, we only care about the first given to us, so we only ask for one. Then, like takeWhile, when there are no more events to come, the Subscription unsubscribes.

    Now, because we merged, the stream will only take whichever event comes first, of either type (the move event that occurred when you could no longer fly, or the damage event where either the damager or the entity was the target player) because the take(1) call is on the merged stream, not any particular one.

    I should also mention that you can easily design this to listen to many types of events simply, without merges. Take this for example:

    [​IMG]

    This is not difficult to implement and is demonstrated in my implementation.

    So, hopefully the power in this is illustrated well by my post, I'll toss up a copy of my implementation but I recommend you attempt to implement your own to get a better understanding of RxJava. Mine will not be packaged, except for in a gist.

    https://gist.github.com/Twister915/bd053583ffbb7a4d1e7f

    The implementation is the bridge between these ideas and Bukkit's API. It provides a class, EventStreamer, which has methods to listen to events.

    Some credit is given to: https://github.com/rmichela/rxjava-bukkit

    I rewrote all of this stuff in my gist myself, but this was my guide when I was first figuring this stuff out myself from scratch. Thanks Ryan Michela, you glorious bastard!
     
    #1 Twister915, Jan 12, 2016
    Last edited: Apr 27, 2016
    • Like Like x 11
    • Winner Winner x 4
  2. This looks really cool, will definitely use this for future projects!
     
    • Friendly Friendly x 1
  3. I have used this very powerful system in a few projects now and I love it. I'm still yet to capture and utilize it's full potential, and even so, it massively improves the way I handle events. I'd recommend everyone give it a go.
     
    • Friendly Friendly x 1
  4. konsolas

    Supporter

    Looks like a really fluid and eloquent system. The main problem I see is the need to shade a 900kb dependency into your jar, and the difficulty in migrating existing event systems within plugins.
     
    • Useful Useful x 1
  5. Nice to see the functional form on this forum. Solid post, dude.
     
    • Friendly Friendly x 1
  6. Very nice! I'm not sure how much use I have for it now, largely due to the size of the dependencies for smaller projects and the effort of converting existing large projects, but I'll keep it in mind for the future. Thanks for taking the time to write this up.
     
    • Useful Useful x 1
  7. I totally recommend this for new projects! However, I should be clear:
    • The depend is 900kb, that is true. I'd argue this is so small it doesn't matter, but it is relatively huge. Keep in mind what we're talking about here people- kilobytes :p
    • This system is non breaking if you use my implementation. All existing plugins are not affected, it only uses vanilla Bukkit API. Check out the EventExecutor class in Bukkit API if you want to see how it works, or just read my code https://gist.github.com/Twister915/bd053583ffbb7a4d1e7f#file-eventstreamer-java-L37-L72
     
    • Useful Useful x 1
  8. Really appreciate the positive feedback man!
     
  9. reserved
     
    • Funny Funny x 1
    • Useful Useful x 1
    • Creative Creative x 1
  10. <3
     
    • Friendly Friendly x 1
  11. FTFY
     
    • Funny Funny x 3
  12. Keep in mind if this is a highly popular resource it could be shaded directly into the Spigot-API, thus eliminating the need of a dependency from child plugins. But let's not get too far ahead of ourselves, shall we?

    // Edit: @Twister915 whilst I know my post is very optimistic and happy, it's usually used as an insult here =p
     
    • Optimistic Optimistic x 1
  13. :(

    Well, I'm one for hoping that it's successful, but I don't know if changing the API is even feasible right now. It certainly could be done in a non breaking manner, but I still don't see it as something everyone's going to be on board with. Let's ask @md_5 :p
     
  14. bahahaha I meant the rating you gave me - "Optimistic" is used as an insult when you don't like someone's post, replacing the previously available "disagree" rating. Your API is great ahaha, don't take that the wrong way (=
     
  15. lol
     
  16. nah I gotcha haha- but yeah optimistic is best rating for that post, fuck the connotation.
     
    • Like Like x 1
    • Optimistic Optimistic x 1
  17. PR into bukkit? That would prevent the shading issue, I'd use it!!
     
  18. Intriguing...will take a look at this
     
  19. I'd do that in an instant, but I don't understand the "shading issue." It's only a few kilobytes lmao- really shouldn't be a problem even though it's relatively huge.
     
  20. Can you still PR since the DCMA? Where do you do it if so?
     
    • Funny Funny x 1

Share This Page