AsyncBlockEvents
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)
Please update for 1.5.2.
Hey, Please update it ( vs 1.5.2)
There's a bug in CraftBukkit that makes it so players can't open dropper inventories with v0.4.0.
Version v0.4.1 has a workaround that makes everything work correctly.
Since I apparently can't link to unapproved downloads in the comments, I'll give you a hint so you can explicitly download it yourself if you so choose to accept the risks, the URL for v0.4.0 was...
http://dev.bukkit.org/server-mods/asyncblockevents/files/7-async-block-events-v0-4-0/
<Removed because file hasn't been approved yet>
IT'S FINALLY HERE, AFTER 2 ***** ***** ***** ****** WEEKS OF **** ***** AND ******* AND BROKEN *******! HUZZAH!
*Censored for your benefit.
1.5 actually broke a lot of stuff and I was rolled in all the fixes in this build. It took some time to get everything right.
Would love a 1.5.1 update.
I don't want to nag you, but 1.5.1 is out...
Sooooo... this project underwent a fairly heavy rewrite since the last update.
The new version, for Minecraft 1.5, (available here: http://dev.bukkit.org/server-mods/asyncblockevents/files/5-async-block-events-v0-3-0/), changes the way events are handled.
Events are now single-pass. Every plugin (not including AsyncBlockEvents) now only receives the event once, in whatever cancellation state as determined by the plugins before it.
AsyncBlockEvents now operates between HIGHEST and MONITOR event priorities. That is, it will act after HIGHEST, but before MONITOR. This ensures perfect plugin compatibility, though it means that it is more sensitive to changes in the CraftBukkit code.
In a similar note, this requires specific tuning for Spigot, which I have done, but it is another thing that can break spontaneously.
UPDATE!
1.5 bukkit update?
Could this plugin prevent block jumping?
Won't any listeners running on "Lowest" "Low", "High" and "Normal" priority act on each event 3 times though? once on the original, once on your async event and again on the final? (Because event processing goes from lowest to highest then monitor)
Decided that the prefire events were actually a real priority given the ease of their implementation. Naturally, I broke the API for good measure.
So, version 0.2, available at bitbucket and here: http://dev.bukkit.org/server-mods/asyncblockevents/files/2-async-block-events-v0-2-0/
As always, if it's not approved, download at your own risk.
@supertycoon0
I understand now, sorry for my lack of knowledge xD
@bob7l
Only the events are asynchronous. If you only read block data from the event (the BlockState) and only set future data to the event (the MaterialData), the engine will do everything synchronously for you.
@RafaSKB
What do you mean? There's a plain (public final) Block as part of every AsyncBlockEvent. That noted, using the block reference to do anything like getType or getData or whatever is unsafe and shouldn't be done. The above comment would also apply, the idea being each event is rich enough so that additional data gathering/setting is unnecessary.
This is awesome! haha Any chance of adding getBlockAt()?
Block place from an asynchronous thread? Doesn't seem like a good idea xD