> 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/pinnaduels/reference/api.md).

# Developer API

Add the PinnaDuels jar as a `provided` dependency and `softdepend: [PinnaDuels]` to your plugin.yml.

Only the `es.edwardbelt.pinnaduels.api` package is public API. Every other class may be renamed or changed between versions (and is obfuscated in released jars), so never import or reflect into them. Keep the API calls in a class that only loads when `Bukkit.getPluginManager().isPluginEnabled("PinnaDuels")` is true.

## PinnaDuelsAPI

All methods are static. While PinnaDuels is off they return null, an empty list, false or 0. Reads only touch memory and are safe from any thread. Methods that act on a player are main thread only.

With MySQL storage only the players online on this server and the best `storage.mysql.top-cache` ranked players are in memory: other offline players read as unknown (`stats` null, `elo` the start ELO, `giveTokens` -1).

| Method                                                                |                                                                                 |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `available()`                                                         | PinnaDuels runs                                                                 |
| `stats(uuid)`                                                         | `DuelStats` (null for a player PinnaDuels never saw)                            |
| `elo(uuid)`, `league(uuid)`, `position(uuid)`                         | ELO, `LeagueInfo`, ranking position (0 = unranked)                              |
| `top(limit)`                                                          | the best ranked players, `List<DuelStats>`                                      |
| `leagues()`, `unrankedLeague()`                                       | the leagues, lowest first                                                       |
| `tokens(uuid)`, `maxTokens()`, `blocksToNextToken(uuid)`              | Duel Tokens                                                                     |
| `giveTokens(uuid, amount)`                                            | add / remove tokens (kept between 0 and the max), returns the new balance or -1 |
| `isInDuel(uuid)`, `duelOf(uuid)`, `spectating(uuid)`, `activeDuels()` | `ActiveDuel` snapshots                                                          |
| `spectate(viewer, duelist)`                                           | puts the viewer in the stands of that duel (main thread)                        |
| `isQueued(uuid)`, `queueSize()`, `queueWaitMillis(uuid)`              | the ranked queue                                                                |
| `joinQueue(player)`, `leaveQueue(player)`                             | like `/duel queue`, same checks and messages (main thread)                      |
| `challenge(from, to)`                                                 | like `/duel <player>` (main thread)                                             |
| `season()`, `seasonEndsAt()`, `seasonMillisLeft()`, `startElo()`      | season info                                                                     |

Read-only records:

* `DuelStats`: uuid, name, elo, peakElo, wins, losses, draws, winrate, streak, bestStreak, placementPlayed, placementMatches, position, league, tokens, weeklyWins; `isPlaced()`, `played()`.
* `LeagueInfo`: id, name, icon (colour codes applied), minElo, top, index; `isRanked()`.
* `ActiveDuel`: id, type (`DuelType` RANKED / GHOST / FRIENDLY / PRACTICE), phase (`DuelPhase` LOADING / COUNTDOWN / RUNNING / RESULTS / CLOSED), arena, sideA, sideB, secondsLeft, countdownSeconds, spectators; `side(uuid)`, `opponentOf(uuid)`. Both enums have a stable `id()`.
* `DuelSide`: uuid (a ghost's = the recorded player), name, elo, score, ghost, player.
* `DuelResult`: uuid, name, score, opponentScore, outcome (1 / 0 / -1), eloBefore, eloChange, counted, forfeited; `won()`, `lost()`, `draw()`.

## Events

| Event                   | When                                                                                                                                                                                                                     |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `DuelStartEvent`        | the countdown began (everyone is in the arena). `getDuel()`                                                                                                                                                              |
| `DuelEndEvent`          | a duel finished and its ELO, stats and rewards were applied. `getDuel()`, `getResults()`, `getResult(uuid)`, `getWinner()` (a `DuelSide`, null on a draw), `isDraw()`. Not fired for duels cancelled before they counted |
| `DuelQueueJoinEvent`    | a player is about to join the ranked queue; cancellable (send your own message)                                                                                                                                          |
| `DuelTokensEarnedEvent` | mining earned Duel Tokens. Asynchronous when fired from PinnaPrison's mining threads. `getPlayer()`, `getEarned()`, `getTokens()`, `getMax()`                                                                            |

All events are in `es.edwardbelt.pinnaduels.api`. The old `es.edwardbelt.pinnaduels.api.event.DuelTokensEarnedEvent` is deprecated: it is still fired (only while something listens to it) and will be removed later.

```java
@EventHandler
public void onDuelEnd(DuelEndEvent event) {
    for (DuelResult result : event.getResults()) {
        if (result.won() && result.eloChange() > 0) {
            Player player = Bukkit.getPlayer(result.uuid());
            if (player != null) player.sendMessage("+" + result.eloChange() + " ELO!");
        }
    }
}
```
