> ## Documentation Index
> Fetch the complete documentation index at: https://items.lodemc.net/llms.txt
> Use this file to discover all available pages before exploring further.

# API events

> Bukkit events Lode Items fires for custom blocks, props, crops, reloads and pack builds, and which vanilla events still fire.

Lode Items fires normal Bukkit events from the `org.lodemc.api.event` package. Listen to them with `@EventHandler` in any registered `Listener`, like any other event. They all fire on the main thread.

| Event | Fires when | Cancellable |
| - | - | - |
| [`CustomBlockPlaceEvent`](#customblockplaceevent) | A player places a custom block | Yes |
| [`CustomBlockBreakEvent`](#customblockbreakevent) | A player breaks a custom block | Yes |
| [`PropPlaceEvent`](#propplaceevent) | A prop is about to be placed | Yes |
| [`PropBreakEvent`](#propbreakevent) | A prop is about to break | Yes |
| [`PropInteractEvent`](#propinteractevent) | A player right-clicks a prop | Yes |
| [`PropStateChangeEvent`](#propstatechangeevent) | A prop switches state | Yes |
| [`CropGrowAttemptEvent`](#cropgrowattemptevent) | A crop gets a growth tick and is about to roll its chance to grow | Yes |
| [`CropGrowEvent`](#cropgrowevent) | A crop is about to change stage, for any reason | Yes |
| [`CropHarvestEvent`](#cropharvestevent) | A ripe crop is about to be picked | Yes |
| [`LodeItemsLoadEvent`](#lodeitemsloadevent) | Lode Items has loaded or reloaded its content | No |
| [`PackBuiltEvent`](#packbuiltevent) | The resource pack was built | No |

Every cancellable event has the usual `isCancelled()` and `setCancelled(boolean)`.

## CustomBlockPlaceEvent

A player placed a custom block, crops included. It fires during Bukkit's `BlockPlaceEvent`, at `HIGHEST` priority, so protection plugins have already allowed it. The block is already in place.

| Method | Returns | What it is |
| - | - | - |
| `getPlayer()` | `Player` | Who placed it |
| `getBlock()` | `Block` | Where |
| `getPlaced()` | [`PlacedBlock`](https://items.lodemc.net/api/blocks#placedblock) | Which custom block, and its state |
| `getItem()` | `ItemStack` | The stack it was placed from |
| `getPlaceEvent()` | `BlockPlaceEvent` | Bukkit's event it fired from |

Cancelling it cancels the `BlockPlaceEvent`, which takes the block back and leaves the item in the player's hand.

```java theme={null}
// Ruby blocks can only be placed below y 0
@EventHandler
public void onPlace(CustomBlockPlaceEvent event) {
    if (event.getPlaced().getId().value().equals("ruby_block") && event.getBlock().getY() >= 0) {
        event.setCancelled(true);
    }
}
```

## CustomBlockBreakEvent

A player is breaking a custom block, crops included. It fires during Bukkit's `BlockBreakEvent`, at `HIGHEST` priority, while the block is still there.

| Method | Returns | What it is |
| - | - | - |
| `getPlayer()` | `Player` | Who's breaking it |
| `getBlock()` | `Block` | Where |
| `getPlaced()` | [`PlacedBlock`](https://items.lodemc.net/api/blocks#placedblock) | Which custom block, and its state |
| `getBreakEvent()` | `BlockBreakEvent` | Bukkit's event it fired from. Use its `setDropItems` and `setExpToDrop` to change what drops. |

Cancelling it cancels the `BlockBreakEvent`, so the block stays. To change the exact items that drop, use Bukkit's `BlockDropItemEvent`.

## PropPlaceEvent

A [prop](https://items.lodemc.net/api/props.md) is about to be placed: by a player, after protection plugins allowed it; by `/lodeitems prop place`; or by [`LodeProps.place`](https://items.lodemc.net/api/props#placing-props). Nothing is in the world yet.

| Method | Returns | What it is |
| - | - | - |
| `getPlayer()` | `Player` | Who's placing it, or `null` for a command or a plugin |
| `getProp()` | [`CustomProp`](https://items.lodemc.net/api/props#customprop) | Which prop |
| `getOrigin()` | `Block` | The block it will stand in |
| `getYaw()` | `float` | Its turn |

Cancel it to stop the placing. A player keeps the item.

## PropBreakEvent

A prop is about to break: a player's hits, after protection plugins allowed it, or [`PlacedProp.breakNaturally`](https://items.lodemc.net/api/props#placedprop).

| Method | Returns | What it is |
| - | - | - |
| `getPlayer()` | `Player` | Who's breaking it, or `null` |
| `getPlaced()` | [`PlacedProp`](https://items.lodemc.net/api/props#placedprop) | The piece |
| `getDrops()` | `List<ItemStack>` | What will drop. You can add, remove and change stacks. It's empty when a player breaks it in creative. |

Cancel it to keep the piece. Storage contents and a jukebox's disc spill out whatever you do with the drops.

## PropInteractEvent

A player right-clicked one of a prop's hitboxes, before the prop does anything: sit, sleep, open storage, open a door, switch state. Protection plugins have already allowed the click.

| Method | Returns | What it is |
| - | - | - |
| `getPlayer()` | `Player` | Who clicked |
| `getPlaced()` | [`PlacedProp`](https://items.lodemc.net/api/props#placedprop) | The piece |
| `getHand()` | `EquipmentSlot` | The hand they clicked with |

Cancel it and the prop does nothing, and the click is used up, so nothing is placed against it either. It doesn't fire when a player sneaks with an item in hand, because that builds against the prop instead of using it.

## PropStateChangeEvent

A prop is switching from one of its `states:` to another: a player's click, a state's `next-after` or `reset-after` timer, or [`PlacedProp.setState`](https://items.lodemc.net/api/props#placedprop).

| Method | Returns | What it is |
| - | - | - |
| `getPlayer()` | `Player` | Who clicked, or `null` for a timer or a plugin |
| `getPlaced()` | [`PlacedProp`](https://items.lodemc.net/api/props#placedprop) | The piece |
| `getFrom()` | `String` | The state it's in now, or `null` for its default look |
| `getTo()` | `String` | The state it's switching to, or `null` for its default look |
| `setTo(String)` | | Changes the state it switches to. It must be one of the prop's states, or `null`. |

Cancel it to keep the current state.

## Crop events in order

When a crop grows by itself:

1. Every [crop rule's](https://items.lodemc.net/api/crops.md#crop-rules) `growthChance` runs.
2. `CropGrowAttemptEvent` fires with the chance the rules returned. Cancel it or change the chance.
3. The chance is rolled. If it fails, nothing more happens this tick.
4. `CropGrowEvent` fires with cause `NATURAL`. Cancel it or change the stage.
5. Bukkit's `BlockGrowEvent` fires, as it does for vanilla wheat. Cancelling it also stops the growth.

Bone meal and plugins skip steps 1 to 3 and 5: they fire only `CropGrowEvent`, with cause `BONE_MEAL` or `PLUGIN`.

## CropGrowAttemptEvent

A crop that isn't ripe got a growth tick, and is about to roll `chance` to grow one stage. Ripe crops don't get growth ticks.

Crops get growth ticks often, so Lode Items only creates this event while at least one plugin listens to it. If you only change the chance and don't need the block or a player, a [crop rule](https://items.lodemc.net/api/crops.md#crop-rules) does the same job more cheaply.

| Method | Returns | What it is |
| - | - | - |
| `getBlock()` | `Block` | Where the crop is |
| `getCrop()` | `CustomCrop` | Which crop |
| `getStage()` | `int` | Its stage now |
| `getLight()` | `int` | Light at its spot. Below the crop's `min-light`, the chance starts at 0. |
| `getGrowthSpeed()` | `double` | Vanilla's growth factor from the farmland around it, times the crop's `speed:` |
| `getChance()` | `double` | The chance (0 to 1) that it grows this tick, after crop rules |
| `setChance(double)` | | Changes the chance. Values outside 0 to 1 are clamped. |
| `setCancelled(boolean)` | | Skips this tick: the crop doesn't grow |

```java theme={null}
// Crops grow 3 times as fast in a greenhouse world
@EventHandler
public void onAttempt(CropGrowAttemptEvent event) {
    if (event.getBlock().getWorld().getName().equals("greenhouse")) {
        event.setChance(event.getChance() * 3);
    }
}
```

## CropGrowEvent

A crop is about to change from stage `from` to stage `to`. Fires for natural growth, bone meal and plugins.

| Method | Returns | What it is |
| - | - | - |
| `getBlock()` | `Block` | Where the crop is |
| `getCrop()` | `CustomCrop` | Which crop |
| `getFrom()` | `int` | Its stage now |
| `getTo()` | `int` | The stage it's about to become |
| `setTo(int)` | | Changes the new stage. It's kept between 0 and the ripe stage. Setting it to `getFrom()` changes nothing. |
| `getCause()` | [`GrowCause`](https://items.lodemc.net/api/crops#growcause) | `NATURAL`, `BONE_MEAL` or `PLUGIN` |
| `setCancelled(boolean)` | | Stops the change. Bone meal used on a crop whose growth you cancel is still used up. |

```java theme={null}
// Bone meal always ripens a crop at once
@EventHandler
public void onGrow(CropGrowEvent event) {
    if (event.getCause() == GrowCause.BONE_MEAL) {
        event.setTo(event.getCrop().getRipeStage());
    }
}
```

> [!TIP]
>
> To stop bone meal working without using it up, return `false` from a rule's [`canBoneMeal`](https://items.lodemc.net/api/crops#crop-rules) instead of cancelling this event.

## CropHarvestEvent

A ripe crop is about to be picked: a player right-clicked it with `harvest:` on, or a plugin called [`LodeCrops.harvest`](https://items.lodemc.net/api/crops#changing-crops).

| Method | Returns | What it is |
| - | - | - |
| `getPlayer()` | `Player` | Who picked it, or `null` when a plugin harvested it without a player |
| `getBlock()` | `Block` | Where the crop is |
| `getCrop()` | `CustomCrop` | Which crop |
| `getDrops()` | `List<ItemStack>` | What pops out. You can add, remove and change stacks. One seed has already been taken off, because it goes back in the ground. |
| `getReplantStage()` | `int` | The stage it starts again at, 0 by default |
| `setReplantStage(int)` | | Changes the stage it starts again at. `-1` removes the crop instead of replanting it. |
| `setCancelled(boolean)` | | Leaves the crop as it is. A cancelled right-click carries on as a normal click, so a held block can still be placed. |

```java theme={null}
// Golden hoes double the harvest; harvesting without one removes the crop
@EventHandler
public void onHarvest(CropHarvestEvent event) {
    Player player = event.getPlayer();
    if (player != null && player.getInventory().getItemInMainHand().getType() == Material.GOLDEN_HOE) {
        List<ItemStack> extra = new ArrayList<>();
        for (ItemStack drop : event.getDrops()) extra.add(drop.clone());
        event.getDrops().addAll(extra);
    } else {
        event.setReplantStage(-1);
    }
}
```

Breaking a crop isn't a harvest. Use Bukkit's `BlockBreakEvent` and `BlockDropItemEvent` for that, and check the block with [`LodeCrops.at`](https://items.lodemc.net/api/crops#finding-crops) first.

## LodeItemsLoadEvent

Lode Items has loaded its content: once when the server starts and again after every `/lodeitems reload`. Items, blocks and crops may have been added, changed or removed, so look up again anything you got from the API before, such as `CustomCrop` objects.

| Method | Returns | What it is |
| - | - | - |
| `getReload()` | `boolean` | `false` for the load when the server starts, `true` for a reload |

The first load happens while Lode Items enables, before your plugin's `onEnable`, so your listener won't see it. Set things up in `onEnable` and use this event for reloads.

```java theme={null}
private CustomCrop tomato;

@Override
public void onEnable() {
    tomato = LodeCrops.crop("tomato_seeds");
    getServer().getPluginManager().registerEvents(this, this);
}

@EventHandler
public void onLodeItemsLoad(LodeItemsLoadEvent event) {
    tomato = LodeCrops.crop("tomato_seeds");   // may be null now if it was removed
}
```

[Crop rules](https://items.lodemc.net/api/crops.md#crop-rules), config keys, types and pack contributors stay registered across reloads, so you don't need to add them again.

## PackBuiltEvent

The resource pack was built: when the server starts and on every reload. It fires right after `LodeItemsLoadEvent`.

| Method | Returns | What it is |
| - | - | - |
| `getFile()` | `Path` | The pack zip, `plugins/LodeItems/pack.zip` |
| `getSha1()` | `String` | Its SHA-1 |
| `getChanged()` | `boolean` | Whether it differs from the last build. When it does, it's being sent to online players. |

Use it to copy the pack somewhere else, or upload it to your own host. To add files to the pack, use a [contributor](https://items.lodemc.net/api/pack.md) instead.

Like `LodeItemsLoadEvent`, the first build happens before your `onEnable`, so you only see rebuilds.

## Vanilla events

These Bukkit events fire for custom blocks and crops as they do for vanilla ones. Bukkit reports a generic `Material` for custom blocks, so use [`LodeItemsApi.blockId`](https://items.lodemc.net/api#lodeitemsapi) or [`LodeCrops.at`](https://items.lodemc.net/api/crops#finding-crops) to tell them apart.

| Event | For |
| - | - |
| `BlockPlaceEvent` | Placing custom blocks and props, and planting crops. For props, the block is the prop's origin and it fires before the prop exists. |
| `BlockBreakEvent`, `BlockDropItemEvent` | Breaking custom blocks and crops. `BlockBreakEvent` also fires for a prop broken by a player, on its origin block. |
| `BlockGrowEvent` | A crop growing by itself, after `CropGrowEvent` |
| `LeavesDecayEvent` | Leaves of a custom tree decaying |
| `PlayerInteractEvent` | Right-clicking custom blocks, crops and props |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.