> For the complete documentation index, see [llms.txt](https://edseries-plugins.gitbook.io/p/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://edseries-plugins.gitbook.io/p/edperks/developers/edperks-api/perk-provider.md).

# PerkProvider SPI

A **PerkProvider** is the integration contract a host plugin implements so its tools can carry EdPerks perks. EdPerks itself is host-agnostic: it owns the perk catalogue, roll engine, GUIs and ticket economy, and reaches every host through this interface. The built-in EdDungeons and EdTools integrations are nothing more than two `PerkProvider` implementations.

```java
package es.edwardbelt.edperks.iapi.provider;

public interface PerkProvider {
    String id();
    boolean isAvailable();
    ItemStack getTool(Player player);
    boolean isTool(ItemStack item);
    void setTool(Player player, ItemStack item);
    default void updateTool(Player player) {}
    default String perkSlot(ItemStack tool) { return null; }
    List<BoostStat> supportedStats();
    void onPerkApplied(Player player, AppliedPerk perk);
    void onPerkCleared(Player player);
    default void openReturnMenu(Player player) {}
    default boolean charge(Player player, String currency, BigDecimal amount) { return false; }
}
```

### Selection

For each player, EdPerks selects the **first available provider** whose `isTool(...)` accepts the player's currently held item. So providers should only claim items they truly manage.

### Threading

All callbacks run on the **server main thread** — they touch the player's inventory.

***

## Methods

#### `String id()`

A stable id for the provider, e.g. `"eddungeons"`.

#### `boolean isAvailable()`

Whether the backing host plugin is present and ready to serve calls **right now**. Gate every host-specific call behind this so EdPerks stays stable if the host is reloaded/absent.

#### `ItemStack getTool(Player player)`

The player's perkable tool (e.g. their held sword / OmniTool), or `null` if they aren't holding one. EdPerks shows this in the centre of the perks menu and stamps the perk NBT onto it.

#### `boolean isTool(ItemStack item)`

Whether `item` is a tool this provider manages.

#### `void setTool(Player player, ItemStack item)`

Persists `item` back as the player's active tool after EdPerks stamped/cleared the perk NBT — typically `player.getInventory().setItemInMainHand(item)`.

#### `void updateTool(Player player)` *(default no-op)*

Refresh the tool item after its perk changed — e.g. re-render the host's lore so the perk line updates. Called right after a perk is applied.

#### `String perkSlot(ItemStack tool)` *(default `null`)*

The storage slot on `tool` under which its perk lives. Single-perk hosts return `null` (one perk per item). A multi-mode host (like the EdTools OmniTool) returns the **active mode id**, so each mode carries its own perk — rolling and reading always target the current mode.

#### `List<BoostStat> supportedStats()`

The stats this platform can boost. This is **advisory** — it drives UI hints, but boosts whose key isn't listed are simply inert here, and hosts may accept any economy key beyond the advertised list. See [`BoostStat`](#booststat).

#### `void onPerkApplied(Player player, AppliedPerk perk)`

Called when a perk becomes (or stays) active on the player's tool. Make your boost system reflect `perk.getBoosts()` for the stats you support. Both built-in hosts translate these into permanent boosters.

#### `void onPerkCleared(Player player)`

Called when the player's tool no longer carries a perk (rolled away, unequipped, switched mode…). Remove the boosters you applied.

#### `void openReturnMenu(Player player)` *(default no-op)*

Open your host's "home" menu — where the perks menu was reached from. Invoked by the menu's `[return]` action / Back button.

#### `boolean charge(Player player, String currency, BigDecimal amount)` *(default refuses)*

Charge the player for the ticket store's `[buy-tickets]` action. Deduct `amount` of `currency` and return `true` on success; return `false` to refuse (the default).

***

## AppliedPerk

The unit passed to `onPerkApplied`:

```java
public final class AppliedPerk {
    String getPerkId();
    String getDisplayName();
    int getLevel();              // 1–5
    Map<String, Double> getBoosts();
    double boost(String statKey); // 0 if not boosted
}
```

Boosts are keyed by [`BoostStat`](#booststat) key, values are decimal multipliers (`0.25` = +25%).

## BoostStat

Declares a stat a host can boost:

```java
new BoostStat("damage", "Damage");                 // key, display name
new BoostStat("attack-speed", "Attack Speed", true); // inverted: higher = better
```

| Field              | Meaning                                                                                                |
| ------------------ | ------------------------------------------------------------------------------------------------------ |
| `getKey()`         | The stable key used in perk configs and NBT (e.g. `damage`).                                           |
| `getDisplayName()` | Human-readable label for lore/UI.                                                                      |
| `isInverted()`     | Whether higher values are *worse* on the raw scale (informational only; EdPerks stores the raw value). |

***

## Minimal example

```java
public final class MyToolProvider implements PerkProvider {

    @Override public String id() { return "mytools"; }

    @Override public boolean isAvailable() {
        return Bukkit.getPluginManager().isPluginEnabled("MyTools");
    }

    @Override public ItemStack getTool(Player player) {
        ItemStack held = player.getInventory().getItemInMainHand();
        return isTool(held) ? held : null;
    }

    @Override public boolean isTool(ItemStack item) {
        return item != null && MyTools.isMyTool(item);
    }

    @Override public void setTool(Player player, ItemStack item) {
        player.getInventory().setItemInMainHand(item);
    }

    @Override public List<BoostStat> supportedStats() {
        return List.of(new BoostStat("money", "Money"),
                       new BoostStat("damage", "Damage"));
    }

    @Override public void onPerkApplied(Player player, AppliedPerk perk) {
        MyTools.setMoneyBoost(player, perk.boost("money"));
        MyTools.setDamageBoost(player, perk.boost("damage"));
    }

    @Override public void onPerkCleared(Player player) {
        MyTools.clearBoosts(player);
    }
}
```

Register it once EdPerks is available:

```java
@Override
public void onEnable() {
    EdPerksAPI api = EdPerksAPI.getInstance();
    if (api != null) {
        api.registerProvider(new MyToolProvider());
    }
}
```

That's it — your tools now appear in the perks menu, roll perks, and apply boosts, reusing EdPerks' entire catalogue and ticket economy.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://edseries-plugins.gitbook.io/p/edperks/developers/edperks-api/perk-provider.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
