AsyncBlockEvents
Note: Since AsyncBlockEvents is a highly technical library, there is only one page of documentation and it is expected you'll read all of it before implementing this library.
AsyncBlockEvents requires ProtocolLib to function.
What is it?
AsyncBlockEvents is a library that allows you to use certain block events asynchronously. The following table shows what's supported:
Event support
Bukkit event | AsyncBlockEvents event | Notes |
---|---|---|
AsyncBlockEvent | Base class for all but AsyncStructureGrowEvent | |
BlockPlaceEvent | AsyncBlockPlaceEvent | New state data includes what is normally "post place" ie stair orientation |
BlockBreakEvent | AsyncBlockBreakEvent | |
PlayerInteractEvent | AsyncBlockInteractEvent | Only some right clicked blocks, see interact support table |
PlayerBucketEmptyEvent | AsyncBucketEmptyEvent | |
PlayerBucketFillEvent | AsyncBucketEmptyEvent | |
StructureGrowEvent | AsyncStructureGrowEvent | Fired only as a result of player bonemeal usage |
Block interact support
Block type | Implementation details |
---|---|
Lever | Change in byte data |
Button | Change in byte data, 20/30 tick callback |
Note block | Change in BlockState |
Wooden door | Dual change in byte data, retransmission of both blocks |
Trap door | Change in byte data |
Fence gate | Change in byte data |
Repeater | Change in byte data |
Cauldron | Change in byte data, consumes item, returns item |
Cake | Change in ID, byte data, fill hunger |
Jukebox | Change in BlockState |
Crafting table | Open inventory |
Enchantment table | Open inventory |
Ender chest | Open inventory |
Chest | Open inventory |
Dispenser | Open inventory |
Furnace | Open inventory |
Brewing stand | Open inventory |
Notably NOT supported | |
Bed | |
Anvil | |
End portal frame |
How does it work?
AsyncBlockEvents works by listening to events in Bukkit, like a normal plugin. However, it has its own event system that reprocesses events. The step by step process:
- Listen for Bukkit events at HIGHEST priority
- "Freeze" block in internal system, preventing other events from firing on block until done processing
- Cancel Bukkit event, so that its effects do not propagate.
- Calculate various asynchronous event details, for example, item drops, future block state
- Fire asynchronous event
- Resend original Bukkit event, modified to match changes to asynchronous event, if possible. Note: Bukkit event is resent 'cancelled', regardless of whether it was received cancelled.
- If event is a 'block place', set type in world
- Listen for Bukkit event at HIGHEST priority
- If Bukkit event was uncancelled when received, or asynchronous event was setCancelled(false) in the Async system, Bukkit event is setCancelled(false)
- If Bukkit event is uncancelled once completed, emulate whatever actions as necessary. This includes playing sounds, setting block types, setting data, firing physics, etc.
- If event is cancelled and event type is a 'block place' set type in world back to old type.
- Unfreeze block
-Note: AsyncBlockEvents also uses ProtocolLibrary to prevent 'blinking' blocks and oddities with having blocks change multiple times in an instant. Events are simulated to normal success. Any change to an event's future state will mean there will be a perceptible time span where the block is the wrong type.
-Note: A synchronous prefire event (to prevent the block 'blip') is in development.
Basically, the event, despite balled in the event system twice, is cancelled for at least one of those passes. The vast majority of plugins ignore cancelled events, so it appears as if the event only happens once for the majority of plugins. of note, AsyncBlockEvents still (re)calls cancelled events, so that behavior is preserved and it is possible to uncancel an event asynchronously.
The is one issue however, if a plugin listening on priority HIGHEST uncancels some events, it can be after AsyncBlockEvents in the event process order. Any other HIGHEST plugin after, and all MONITOR plugins with then receive the uncancelled event twice. In my experience, uncancelling is really quite rare, and is generally unsuitable for HIGHEST priority, so the vast majority of plugins should still work. However, is must be stressed that AsyncBlockEvents is a massive hack, correct behavior is not guaranteed nor can be.
Why would I want to use this?
Flexibility. By decoupling events form the synchronous system, you can freely perform expensive tasks relating to block events with blocking the main thread. Say you wanted a plugin that set the color of wool placed as determined by pulling the "color of the minute" from a website. Synchronously blocking while you GET the color would lead to a pause in the main thread and create lag. What if the website was overloaded and had 5 second return times? Leave just one block in limbo, not the whole server.
-Obviously, you could schedule something to fetch the color and then change the block when it gets a response, but then you would be doing essentially what this plugin does (although AsyncBlockEvents has a much more generic handler). AsyncBlockEvents allows you to asynchronously handle events without even considering the implications of scheduling.
Additionally, by emulating these events, AsyncBlockEvents exposes fields that are normally not available. The base AsyncBlockEvent supports future state, experience, and item drops.
-What to drop xp if a user places a block? Easy. No need to schedule a synchronous task to ensure a plugin didn't cancel the even on you then have to spawn xp orbs yourself, just set the exp to drop to 10. If the event's cancelled later on, the xp won't drop. Simple.
-Want to change what happens if a user breaks a block? Maybe mining is too easy; maybe you want stone to 'break' into cobblestone before it can be mined out. Trying to implement this yourself would be almost impossible. (Worth nothing, so was AsyncBlockEvents) The cobble would still drop, you'd still have to set the block manually yourself, and that would be ignoring the implications of the event being cancelled later on! With AysncBlockEvents, simply set the future state to be a cobblestone block, set the drop to an empty list, and poof, miserable players all around.
Miscellaneous notes
- Everything is completely emulated. The only NMS call is to applyPhysics, which is excluded from Bukkit.
- AsyncBlockEvents is not version stable. All block logic is hardcoded, and there's the above native call.
- Changing the future block type for interact events will result in the interact being performed on the future block type. For example, if you had a user right click on a chest and it becomes an ender chest, his ender chest inventory will be opened, not the old chest.
- Changing the future block type to a multiblock entity (door or bed) NOT in a BlockPlace event will result in a stub.
- All interact events on wooden doors are recomputed so that it acts like the player always clicks the bottom half of the door (for AsyncBlockInteract events only; the Bukkit event isn't modified)