[LIB] AsyncBlockEvents -- Providing full control of block events

Discussion in 'Resources' started by super_tycoon, Feb 27, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    super_tycoon

    AsyncBlockEvents -- @BukkitDev

    AsyncBlockEvents is library designed to give plugin developers the ability to not only asynchronously control block events, but to also allow direct manipulation of event properties not otherwise exposed by the Bukkit API.

    Quite simply, AsyncBlockEvents adds two new event type paradigms (or something) to Bukkit. It adds, for example, AsyncBlockBreakEvent and PrefireBlockBreakEvent.

    Asynchronous block events are just as they are named. They are events that are called asynchronously and allow you to change the world state without having to use the scheduler and without having to stall the main thread.

    Warning: Asynchronous events are just that. You are limited to what the (AsyncBlockEvents) API provides, or using the Bukkit scheduler to run code synchronously if you need to interact with the world.

    You could, for example, perform expensive database operations and change the fate of the block without causing massive amounts of lag. Only the one block will be held in limbo, everything else is free to proceed.

    Prefire block events are fired, synchronously, before the async block event is called. These give you full access to the powerful custom block events while still being run synchronously. The result of these events is displayed (ie sent as a packet to players) after prefire event completion, however an Async block event listener can further change the event, and those changes will eventually be the one's that stick. No blocks are actually changed in-world in modifying a prefire event.

    For example, say you have a plugin that gets a player's favorite color from a website and changes all placed wool to that color. You can do this asynchronously, but it would also be nice to predict which color it would be (say the player almost never changes it) and eliminate the white-to-color 'blip'. With the prefire event, just set the future block to the cached color and make the process as seamless and lag-free as possible to the user.

    Additionally, the prefire events can be used alone for previously difficult / impossible tasks. Say you wanted a plugin that made all smooth stone mined to turn into cobblestone first? Normally, you could just set the Material to AIR, but then you'd lose out on breaking effects and sound. With this, you can simply set the future state in the prefire event, and assuming nobody else interferes, a cobblestone block will appear as you break stone.

    ________________________________________________________________

    Event details

    Every AsyncBlockEvent event has at least the following properties:
    • Block block - the block in question - read only
    • BlockState oldState - the captured, old state of the block - read only
    • Player player - the player - read only
    • ItemStack hand - the ItemStack the player had in his hand the moment of the event - read only though modifiable
    • int Exp - a free to assign int - It's possible to spawn XP orbs for any block event you'd like. Negative values also work to lower player xp
    • getNewMatData() - MaterialData - gets the default, new state of the block - includes what is normally a "post-place" calculation like stairs
    • setNewState(MaterialData) - Set the future state of the block to whatever you'd like without breaking animations or sound effects
    • getDrops() - read-only Collection<ItemStack> - Gets the true drops of the event - includes the effects of fortune and silk-touch
    • setDrops(Collection<ItemStack>) - Set drops to whatever you want, for whatever event you want
    • Cancellable - Events are always thrown, cancelled events are still propagated, and it's even possible to resurrect an event cancelled in the normal, synchronous event chain.
    ________________________________________________________________

    Real Examples

    Asynchronous block events

    The plugin MyBlock uses asynchronous block events to cancel unpermitted block places / breaks / interacts. Searching a database, while quick, adds up when doing thousands of times. Using the asynchronous events keeps the TPS up and prevents the plugin from halting the server potentially hundreds of times per second.

    It's as easy as AsyncBlockPlaceEvent event.setCancelled(true);

    Prefire block events

    A simple example showcasing the power of the prefire events is included below:
    Code:java
    1. public class AsyncDemo extends JavaPlugin implements Listener {
    2. @Override
    3. public void onEnable() {
    4. Bukkit.getPluginManager().registerEvents(this, this);
    5. }
    6.  
    7. @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
    8. public void onBlockBreak(final PrefireBlockBreakEvent event) {
    9. if (event.oldState.getType() == Material.STONE && !event.hand.containsEnchantment(Enchantment.SILK_TOUCH)) {
    10. event.setNewState(Material.COBBLESTONE.getNewData((byte) 0));
    11. event.setDrops(AsyncBlockEvents.EMPTY_STACK);
    12. }
    13. }
    14. }

    As you can simply see, this tiny plugin (sans yml) sets stone blocks on break to cobblestone, implementing the imaginary example given earlier. A video demo of what this looks like is embedded below:



    ________________________________________________________________

    Limitations


    AsyncBlockEvents overrides the server's PluginManager. Any other plugins that perform something similar may not function with AsyncBlockEvents, or more likely, break it.

    While block breaks, places, and bucket empties and fills are completely supported, player interactions are limited to...

    levers, buttons, note blocks, wooden doors, trap doors, fence gates, repeaters, cauldrons, cake, jukeboxes, crafting tables, enchantment tables, ender chests, chests, dispensers, furnaces, brewing stands, trapped chests, hoppers, droppers, and comparators

    Meaning that the event will only be called when a player right clicks on the one of the above. It is not possible, say for a player to click on a dirt block and have it turn to grass. It could be supported in the future, but with a more limited API.

    AsyncBlockEvents also requires ProtocolLib to function.

    ________________________________________________________________

    Source code / licensing

    AsyncBlockEvents is Open Source Software.

    It is stored in a BitBucket repo here. (Which also has a handy wiki, downloads, and the issue tracker)

    AsyncBlockEvents is licensed under the Simplifed BSD License (aka 2-clause).
     
    Maulss, Wingzzz and WarmakerT like this.
  2. Offline

    mbaxter ʇıʞʞnq ɐ sɐɥ ı

    You should probably point out in your documentation here that you're also cancelling all relevant normal bukkit events, which in turn means that this plugin breaks many logging plugins.
     
  3. Offline

    super_tycoon

    Oh, that's a good reminder, it actually only breaks (potentially, but that's just as bad) plugins that uncancel events listening on HIGHEST. MONITOR plugins work fine, I used my own logging plugin as an acid test.

    I actually rethrow all vanilla events to completion. I'll draw up a graph, the event model is.. quite messy if you look right at the guts. I knew I was forgetting something.
    AsyncBlockEvents, as of version 0.3.0 will actually inject a new PluginManager and override the event processing so that all events are single pass. Whether this is better or not is a matter of taste I suppose. But it does mean that plugins should have perfect compatibility now. Spigot is a tad touchy, but I also inject some extra magic for compatibility with it as well.
     
Thread Status:
Not open for further replies.

Share This Page