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

# Cooldowns & Scheduler

> Generic cooldown tracking and a Folia-safe scheduler wrapper.

## `CooldownManager`

`xyz.refineryteam.refinerycore.api.cooldown.CooldownManager` tracks arbitrary cooldowns keyed by `(namespace, key, subject)`. The `namespace` is typically your plugin or feature name (e.g. `"kits"`, `"combat"`), so two plugins can use the same subject/key without colliding.

<Note>
  The [command framework](/refinerycore/commands#cooldown) already uses an internal `CooldownManager` for the `@Cooldown` annotation — you only need to use this class directly for cooldowns outside of commands (ability usage, kit claims, etc).
</Note>

```java KitsManager.java icon="java" theme={"system"}
CooldownManager cooldowns = new CooldownManager();

if (cooldowns.isOnCooldown("kits", "starter", player.getUniqueId())) {
    long remaining = cooldowns.remainingSeconds("kits", "starter", player.getUniqueId());
    context.reply("<red>Wait " + remaining + "s before using this again.");
    return;
}

cooldowns.set("kits", "starter", player.getUniqueId(), Duration.ofMinutes(30));
```

| Method                                          | Description                                                                                                      |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `set(namespace, key, subject, Duration)`        | Starts (or overwrites) a cooldown                                                                                |
| `setSeconds(namespace, key, subject, long)`     | Shorthand for `set` with a `Duration.ofSeconds(...)`                                                             |
| `isOnCooldown(namespace, key, subject)`         | Whether the cooldown is still active (auto-expires and returns `false` once elapsed)                             |
| `remainingMillis` / `remainingSeconds`          | Time left, or `0` if not on cooldown                                                                             |
| `tryAcquire(namespace, key, subject, Duration)` | Atomically checks-and-sets: starts the cooldown **only if** not already on one, returning `true` if it proceeded |
| `clear(namespace, key, subject)`                | Removes a specific cooldown                                                                                      |
| `clearAll(subject)`                             | Removes every cooldown (any namespace/key) for a subject, e.g. on player quit                                    |

`tryAcquire` is the recommended entry point for most "can this action happen" checks, since it avoids a separate check-then-set race.

## `RefineryScheduler`

`xyz.refineryteam.refinerycore.api.scheduler.RefineryScheduler` wraps Paper's **Folia-compatible** region/entity/async schedulers behind a flat set of static methods, so plugin code doesn't need to branch between the legacy `BukkitScheduler` and Folia's region-based scheduler APIs.

```java EntityScheduler.java icon="java" theme={"system"}
RefineryScheduler.runSync(plugin, () -> {
    // runs on the global region scheduler
});

RefineryScheduler.runAsync(plugin, () -> {
    // runs on Paper's async scheduler
});

RefineryScheduler.runForEntity(plugin, someEntity, () -> {
    // runs on the entity's own scheduler — required on Folia for entity-specific logic
});
```

| Method                                                                     | Description                                                                                                           |
| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `runSync(plugin, Runnable)`                                                | Runs once on the global region scheduler                                                                              |
| `runSyncDelayed(plugin, Runnable, delayTicks)`                             | Same, after a tick delay                                                                                              |
| `runSyncRepeating(plugin, Runnable, delayTicks, periodTicks)`              | Repeating task on the global region scheduler                                                                         |
| `runAsync(plugin, Runnable)`                                               | Runs once on Paper's async scheduler                                                                                  |
| `runAsyncDelayed(plugin, Runnable, delayTicks)`                            | Same, after a delay (converted internally to milliseconds)                                                            |
| `runAsyncRepeating(plugin, Runnable, delayTicks, periodTicks)`             | Repeating async task                                                                                                  |
| `runForEntity(plugin, entity, Runnable)`                                   | Runs once on the given entity's own scheduler — required for entity-specific logic under Folia's per-region threading |
| `runForEntityRepeating(plugin, entity, Runnable, delayTicks, periodTicks)` | Repeating variant of the above                                                                                        |
| `cancel(ScheduledTask)`                                                    | Cancels any task returned by the methods above                                                                        |

<Warning>
  Prefer `RefineryScheduler` over calling `Bukkit.getScheduler()` directly anywhere your plugin might run on Folia. Mixing the legacy `BukkitScheduler` with Folia's region schedulers in the same plugin is a common source of `IllegalStateException`s on Folia servers.
</Warning>
