question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Reimplement Events using the Observable pattern

See original GitHub issue

Events are useful for interacting with Minecraft in a portable way. Unfortunately, events in their current form are unsuitable for the Observer pattern, since events can consist of multiple arguments and don’t provide a direct way to modify the event as it is being propagated to listeners. I suggest reimplementing all Events to conform to the Observer pattern, by placing the input arguments into a record-like class. Note that this suggestion applies to the internal implementation only, the external API can remain the same (Although obviously, we should allow mods direct access to the record-like if they so desire, and perhaps the multi-argument lambda functions should eventually be removed by a deprecation cycle, although that is a separate issue).

A couple of advantages for Observables over Events:

  1. Additional methods can be added for convenience or additional functionality (a Event#cancel(); or e.g. ChatMessageEvent#changeMessage(message -> toUpperCase(message));)
  2. Adding additional inputs or methods later on will not break any existing code.
  3. Generating events or event listeners is much simpler, and doesn’t require Reflection or ASM, @Annotations would suffice.
  4. All Observable operators can be applied as usual, e.g. WorldTickEvent.filter(event -> isOverworld(event.getWorld())). In the current system, operators like map cannot be implemented. In general, Observables are much nicer to deal with because of this.
  5. We can provide native (“hardware-accelerated”) support for common operations, e.g. we could implement the above filter as WorldTickEvent.filter(OVERWORLD_FILTER), such that if 20 mods apply this filter, the code for it will only have to be invoked once per event, instead of 20 times using 20 different if statements.
  6. Different parts of a listener can be executed asynchronously on different threads if desired, much like .stream() in Java.

A couple of concerns debunked:

Q: Doesn’t wrapping inputs in record-likes create unnecessary allocations? A: No. The record-likes can be cached between listeners, and between events for non-recursive events (Which is most events). This is also why it is important for Fabric to provide this functionally itself, because other mods can’t reliably cache events themselves (without mixing into Fabric, of course), meaning that even if an external mod were to implement Observables for themselves by manually wrapping the inputs into a record-like, the implementation would still be much less efficient.

Q: Can’t listeners unexpectedly mutate the events they are passed? A: No. record-like members can be made final, or record-likes can instead be implemented as interfaces with getter methods.

Q: Aren’t Observables much slower than the current system? A: No. Internally, Observables are implemented the same way as the current system, with the only difference being that only listeners that implement Consumer<T> are allowed. Listeners can cache the inputs into their own local variables, meaning the overhead per event only has to be a single GETFIELD copy per listener, and you have to remember that this copy overhead already exists in the current system because the arguments have to be copied to the stack for every method invocation. Observer listeners only have to copy the fields that they actually use, whereas the current system always needs to copy all inputs for every listener invocation.

Q: How can we cache record-likes if their contents are final? A: We can use ASM or Reflection to change the contents once per event.

Q: Registering a lambda with multiple arguments is much more convenient! A: We can still keep supporting the old system with minimal overhead.

Q: Wouldn’t implementing an entire Reactive Library for fabric require lots of maintenance? A: Although it would be nice for performance tuning, Fabric doesn’t strictly have to provide any Observable methods itself, libraries like RxJava provide adapter methods to turn Events into propper Observables. The only change that is required to come from fabric is to limit listener inputs to one object.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:8
  • Comments:9 (8 by maintainers)

github_iconTop GitHub Comments

4reactions
LemmaEOFcommented, Feb 5, 2021

You’re currently proposing a change that would fundamentally break the entire event architecture of Fabric. While I’m not fundamentally opposed to fundamental reworks, you have yet to provide any example code of how this would work, much less an analysis of its performance impact. This is a forge-y way of doing events that is incompatible with the Fabric ethos. If you really want this, bring it back as a PR so we can audit and test it. I’m using my authority as a member of the triage team to close this, as further bickering won’t do anything unless we have real code to inspect.

4reactions
Technici4ncommented, Feb 4, 2021

Please, I want to see actual code, I don’t think throwing around concepts and arguments like this is very useful. 😉

Read more comments on GitHub >

github_iconTop Results From Across the Web

Implementing the Observer Pattern with an Event System | Libelli
The basic pattern is that we have a subject that contains some state. Other objects called observers register themselves with the subject and ......
Read more >
Patterns: Observable pattern - DEV Community ‍ ‍
This pattern is very useful when there are many software components attached to a specific event. In that case, those components just need...
Read more >
How to Implement Observer Pattern Using SpringBoot Events
The goal of this article is to give you a quick understanding of the Observer Pattern, its benefits and how to implement it...
Read more >
c# - Implementing observer pattern with events - Stack Overflow
Events are an implementation of the observer pattern. An event is implemented as a list of methods to be called when the vent...
Read more >
Observer Design Pattern | Microsoft Learn
Observers register with the provider, and whenever a predefined condition, event, or state change occurs, the provider automatically notifies ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found