Skip to content

Developer API

Jan Kluka edited this page Aug 17, 2026 · 37 revisions

Developer API

The X-Prison public API lives in a dedicated project: X-PrisonAPI on GitHub


Adding X-PrisonAPI as a Dependency

Add the API artifact to your plugin's build tool. The API is published to the Drawethree Maven repository.

Maven:

<repository>
    <id>drawethree-repo</id>
    <url>https://repo.drawethree.dev/releases</url>
</repository>

<dependency>
    <groupId>dev.drawethree</groupId>
    <artifactId>X-PrisonAPI</artifactId>
    <version>LATEST</version>
    <scope>provided</scope>
</dependency>

Gradle (Kotlin DSL):

repositories {
    maven("https://repo.drawethree.dev/releases")
}

dependencies {
    compileOnly("dev.drawethree:X-PrisonAPI:LATEST")
}

Declare X-Prison as a dependency in your plugin.yml:

depend: [X-Prison]
# or if optional:
softdepend: [X-Prison]

Accessing the API

import dev.drawethree.xprison.api.XPrisonAPI;

XPrisonAPI api = XPrisonAPI.getInstance();

Each module exposes its own sub-API through the main API instance:

api.getEnchantsApi()      // Enchant system
api.getRanksApi()         // Ranks
api.getPrestigesApi()     // Prestiges
api.getRebirthApi()       // Rebirths
api.getBlocksApi()        // Block tracking
api.getCurrencyApi()      // Currency balances
api.getGangsApi()         // Gangs
api.getMultipliersApi()   // Global, player, and rank multipliers
api.getAutoSellApi()      // Auto-sell prices and regions
api.getMinesApi()         // Mine management
api.getBombsApi()         // Bomb items
api.getAutoMinerApi()     // Auto-miner time
api.getBattlePassApi()    // Battle Pass tiers, XP and premium
api.getQuestsApi()        // Quests progress and rerolls
api.getDailyRewardsApi()  // Daily login streaks and claims

Battle Pass / Quests / Daily Rewards each expose read and write access plus their own Bukkit events (e.g. BattlePassXpGainEvent, BattlePassTierUpEvent, QuestCompleteEvent, QuestClaimEvent, PlayerDailyRewardClaimEvent):

// Battle Pass
api.getBattlePassApi().addXp(uuid, 500, XpSource.OTHER);
int tier = api.getBattlePassApi().getTier(uuid);
boolean premium = api.getBattlePassApi().isPremium(uuid);

// Quests
List<ActiveQuest> daily = api.getQuestsApi().getActiveQuests(uuid, QuestCategory.DAILY);
api.getQuestsApi().addProgress(uuid, "daily_mine_blocks", 100);

// Daily Rewards
int streak = api.getDailyRewardsApi().getStreak(uuid);
boolean claimed = api.getDailyRewardsApi().claim(uuid);

Dashboard integration (requires Dashboard addon):

api.setDashboardUrl(String url)   // called by the Dashboard addon on startup
api.getDashboardUrl()             // returns the active panel URL, or null if not running

Module and addon management:

api.getModules()                        // List<XPrisonModule> — all registered modules
api.isModuleEnabled(String configKey)   // true if module is active
api.enableModule(String configKey)
api.disableModule(String configKey)

api.getLoadedAddons()                          // List<XPrisonAddon>
api.enableAddon(String name)
api.disableAddon(String name)
api.loadAddonFromFile(File jar)                // runtime load without restart

Building an Addon (XPrisonAddonContext)

Addons are JARs placed in plugins/X-Prison/addons/. Declare the main class and metadata in the JAR manifest:

X-Prison-Addon-Class: com.example.MyAddon
X-Prison-Addon-Name: MyAddon
X-Prison-Addon-Description: Does something useful
X-Prison-Addon-Version: 1.0.0
X-Prison-Addon-Author: YourName
X-Prison-Min-Version: 2026.2.2.4-BETA
X-Prison-Priority: 50
X-Prison-Depends: OtherAddon, AnotherAddon
Attribute Required Description
X-Prison-Addon-Class Yes Fully-qualified class name implementing XPrisonAddon
X-Prison-Addon-Name No Display name shown in the addon manager GUI and logs
X-Prison-Addon-Version No Version string
X-Prison-Addon-Author No Author name
X-Prison-Addon-Description No Short description
X-Prison-Min-Version No Minimum X-Prison version (e.g. 2026.2.0). A warning is printed if the running version is older.
X-Prison-Priority No Integer load-order priority. Lower values load first. Default: 50.
X-Prison-Depends No Comma-separated list of addon names that must be loaded before this one. The addon is skipped if any dependency is missing.

Implement XPrisonAddon:

public class MyAddon implements XPrisonAddon {

    private XPrisonAddonContext context;

    @Override
    public void onEnable(XPrisonAddonContext context) {
        this.context = context;

        XPrisonAPI api = context.getAPI();           // full X-Prison API
        File dataFolder = context.getDataFolder();   // plugins/X-Prison/addons/MyAddon/
        Logger log = context.getLogger();            // prefixed logger
        String name = context.getAddonName();
        String version = context.getAddonVersion();

        // Register Bukkit event listeners
        context.registerEvents(new MyListener());

        log.info("MyAddon enabled.");
    }

    @Override
    public void onDisable() {
        context.getLogger().info("MyAddon disabled.");
    }
}

Tip: Use context.getDataFolder() for your own config files. Do not read or write to X-Prison's own data folder.


Addon Load Order

X-Prison resolves load order across all addon JARs before instantiating any of them. The rules, in priority order:

  1. Dependencies first — if your manifest declares X-Prison-Depends: CoreAddon, CoreAddon is always loaded before your addon, regardless of priority numbers.
  2. Lower priority number loads first — among addons with no dependency relationship, the one with the smaller X-Prison-Priority value loads first. Default is 50.
  3. Alphabetical tiebreak — addons sharing the same priority and no dependency relationship are loaded alphabetically by name for determinism.

Skipped addons — an addon is skipped (with a console warning) if:

  • A declared dependency is not present in the addons folder.
  • A dependency was itself skipped (cascade).
  • The addon is part of a circular dependency chain (A depends on B, B depends on A).

Unload order is the exact reverse of load order — dependents are always shut down before the addons they rely on.

Example manifest for an addon that must load after a hypothetical CoreAddon foundation:

X-Prison-Addon-Class: com.example.MyFeatureAddon
X-Prison-Addon-Name: MyFeatureAddon
X-Prison-Addon-Version: 1.0.0
X-Prison-Depends: CoreAddon
X-Prison-Priority: 60

Registering a Custom Enchant

Custom enchants must extend XPrisonEnchantment and are driven by JSON config files (placed in plugins/X-Prison/enchants/), just like built-in enchants.

@Override
public void onEnable(XPrisonAddonContext context) {
    MyCustomEnchant enchant = new MyCustomEnchant();
    context.getAPI().getEnchantsApi().registerEnchant(enchant);
}

@Override
public void onDisable() {
    // unregister if needed
}

Your enchant class overrides these methods:

Method Called when
onEquip(Player, ItemStack, int) Player picks up or equips the pickaxe
onUnequip(Player, ItemStack, int) Player removes or drops the pickaxe
onBlockBreak(BlockBreakEvent, int) A block is broken in a mine with this enchant active
reload() The plugin is reloaded — re-read your config values here

Area (multi-block) enchants

If your enchant breaks many blocks at once, extend AreaBreakEnchant (dev.drawethree.xprison.api.enchants.area) instead of implementing onBlockBreak yourself. You override selectTargets(...) to say which blocks are affected; drops, auto-sell, Fortune, prestige scaling, currency payout, the proc message, mine-reset accounting, pickaxe progression and packet-mine support are all handled by the shared pipeline.

Full guide, including the available hooks and the eventStrategy performance trade-off: Area Enchants: AreaBreakEnchant.

Building your own break pipeline (1.9)

If you cannot extend AreaBreakEnchant — for example your enchant already extends something else — these API methods expose the individual pieces. All of them are default, so an addon compiled against 1.9 still loads on an older core (the feature simply becomes a no-op).

XPrisonEnchantsAPI enchants = api.getEnchantsApi();

// The cuboid an area enchant may affect (highest-priority region that permits enchants).
Optional<AreaBounds> region = enchants.getEnchantRegionBounds(location);

// Fortune level on a pickaxe, and blocks Fortune must not multiply.
int fortune = enchants.getItemFortuneLevel(pickaxe);
boolean skip = enchants.isFortuneBlacklisted(block);

// Suppress X-Prison's own gated listeners for a synthetic BlockBreakEvent you fire.
enchants.ignoreBlockBreakEvent(event);

XPrisonBlocksAPI blocks = api.getBlocksApi();

// Run the shared post-break pipeline once for a set of blocks: blocks-broken statistic,
// lucky blocks, and the aggregate XPrisonBlockBreakEvent that quests / battle pass /
// block boosters consume. Call this ONLY when you are not firing a Bukkit event per
// block — otherwise X-Prison's own listener already ran it and you would double-count.
blocks.handleBlockBreak(player, brokenBlocks, countBlocksBroken);

// Bulk form for breaks too large to enumerate (a whole packet mine): O(block types).
blocks.handleBulkBlockBreak(player, typeCounts);

// Credit the pickaxe with blocks and XP from a named source.
api.getPickaxeLevelsApi()
   .addBlocksAndExp(player, pickaxe, blockCount, exp, PickaxeExpSource.AREA_ENCHANTS);

// Read a pickaxe's permanent quality tier and what it multiplies. Absent tag = tier 0 = 1.0x.
XPrisonPickaxeQualityAPI quality = api.getPickaxeQualityApi();
double tokenBonus = quality.getCurrencyMultiplier(pickaxe, "tokens");

// PlayerPickaxeQualityUpgradeEvent is cancellable and its cost is mutable, so an addon can
// block an upgrade or discount it before the player is charged.

// Currencies can be capped, so credit and report what was ACTUALLY added — otherwise a
// "you earned %amount%" message overstates the payout whenever the cap clamps it.
BigDecimal credited = api.getCurrencyApi()
        .addBalance(player, currency, amount, ReceiveCause.MINING);

// Drops belong in the backpack, not the inventory, when this is on. Note UltraBackpacks
// reads real world state, so bypass it whenever the affected blocks are virtual.
boolean backpacks = api.isUltraBackpacksEnabled();

Text API

Use this instead of depending on Adventure directly. X-Prison does not shade Adventure — it uses the copy Paper bundles, and on servers that provide none (Spigot, CraftBukkit) it renders MiniMessage down to classic colour codes instead. An addon that imports net.kyori itself therefore works on Paper but fails to link on Spigot, and nothing warns you at compile time, because Paper's API pulls Adventure in transitively.

XPrisonTextAPI text = api.getTextApi();

// Sending — PlaceholderAPI placeholders are resolved for you
text.sendMessage(player, "<gradient:#FFD700:#FFAA00>Well mined!</gradient>");
text.sendMessage(player, List.of("<gray>Line one", "<gray>Line two"));
text.sendActionBar(player, "<green>+100 tokens");
text.sendTitle(player, "<gold><bold>LEVEL UP", "<gray>You reached level %level%", 10, 40, 10);

// Rendering to a String, for sinks that take coloured text rather than sending it
String hologramLine = text.toLegacySection(configLine);  // legacy section codes
String plain        = text.stripTags(gangName);          // no tags at all

// Feature check — hover and click cannot be shown on Spigot
if (text.isRich()) {
    text.sendMessage(player, "<click:open_url:'https://example.com'>Click me</click>");
} else {
    text.sendMessage(player, "<gray>Visit https://example.com");
}
Method Description
sendMessage(CommandSender, String) Send one message
sendMessage(CommandSender, List<String>) Send several lines
sendActionBar(Player, String) Send an action bar message
sendTitle(Player, String, String, int, int, int) Title, subtitle and timings in ticks
toLegacySection(String) Render to legacy § codes for holograms, scoreboards, PAPI returns
stripTags(String) Plain text, for length limits and name comparisons
isRich() true when hover, click and fonts are available (Paper-family servers)

Pass raw config text. MiniMessage tags and legacy & codes are both accepted, and X-Prison renders once, for whichever server it is on. Do not pre-render before passing it in — rendering twice is how player-supplied values (names, nicknames) end up recolouring the message around them.

Never use ChatColor.stripColor on config text. It does nothing to MiniMessage tags, so a name written as <gradient:#FFD700:#FFAA00>Elite</gradient> measures 5 characters through stripTags() and 46 through stripColor. Use stripTags() for every length check and name comparison.

All methods are thread-safe and never throw on malformed input.


Ranks API

XPrisonRanksAPI ranks = api.getRanksApi();

// Online and offline reads
Rank rank = ranks.getPlayerRank(player);
Rank rank = ranks.getPlayerRankOffline(uuid);           // DB lookup, works for offline players
Rank next = ranks.getNextPlayerRank(player);
double progress = ranks.getRankupProgress(player);      // 0.0 – 1.0
boolean isMax = ranks.isMaxRank(player);

// Writes
ranks.setPlayerRank(player, rank);
ranks.setPlayerRankOffline(uuid, rank);                 // persists to DB immediately
ranks.resetPlayerRank(player);

// Leaderboard
List<RankLeaderboardEntry> top = ranks.getTopByRank(10);

// All player UUIDs that have rank data
Set<UUID> uuids = ranks.getAllPlayerUUIDs();

// Rank list
List<Rank> allRanks = ranks.getAllRanks();
Rank max = ranks.getMaxRank();
Rank defaultRank = ranks.getDefaultRank();
Rank byId = ranks.getRankById(id);

Prestiges API

XPrisonPrestigesAPI prestiges = api.getPrestigesApi();

Prestige prestige = prestiges.getPlayerPrestige(player);
Prestige prestige = prestiges.getPlayerPrestigeOffline(uuid);   // offline lookup
double progress = prestiges.getPrestigeProgress(player);
boolean isMax = prestiges.isMaxPrestige(player);
Prestige next = prestiges.getNextPlayerPrestige(player);

prestiges.setPlayerPrestige(player, prestige);
prestiges.setPlayerPrestigeOffline(uuid, prestige);             // persists to DB immediately
prestiges.resetPlayerPrestige(player);

List<PrestigeLeaderboardEntry> top = prestiges.getTopByPrestige(10);
Set<UUID> uuids = prestiges.getAllPlayerUUIDs();

List<Prestige> all = prestiges.getAllPrestiges();
Prestige max = prestiges.getMaxPrestige();

Rebirth API

XPrisonRebirthAPI rebirth = api.getRebirthApi();

Rebirth r = rebirth.getPlayerRebirth(player);
Rebirth r = rebirth.getPlayerRebirthOffline(uuid);   // offline lookup
boolean isMax = rebirth.isMaxRebirth(player);
Rebirth next = rebirth.getNextPlayerRebirth(player);

rebirth.setPlayerRebirth(player, r);
rebirth.setPlayerRebirthOffline(uuid, r);            // persists to DB immediately
rebirth.resetPlayerRebirth(player);
rebirth.tryRebirth(player);                          // attempts rebirth, checks requirements

List<RebirthLeaderboardEntry> top = rebirth.getTopByRebirth(10);
Set<UUID> uuids = rebirth.getAllPlayerUUIDs();

List<Rebirth> all = rebirth.getAllRebirths();
Rebirth max = rebirth.getMaxRebirth();

Currency API

XPrisonCurrencyAPI currency = api.getCurrencyApi();

// Balance operations — all work for online AND offline players
BigDecimal balance = currency.getBalance(uuid, currencyName);
currency.addBalance(uuid, currencyName, amount);
currency.removeBalance(uuid, currencyName, amount);    // clamped to 0
currency.setBalance(uuid, currencyName, amount);
boolean has = currency.has(uuid, currencyName, amount);
currency.transferBalance(fromUuid, toUuid, currencyName, amount);

// Leaderboard
List<CurrencyLeaderboardEntry> top = currency.getTopByBalance(currencyName, 10);
List<CurrencyLeaderboardEntry> top = currency.getTopByBalance(currencyName, 10, offset);

// Currency CRUD (live — changes apply immediately and persist to currencies.yml)
currency.createCurrency(name, displayName, prefix, suffix, format, startingBalance);
currency.updateCurrency(name, displayName, prefix, suffix, format);
currency.deleteCurrency(name);
XPrisonCurrency c = currency.getCurrency(name);
List<XPrisonCurrency> all = currency.getAllCurrencies();

Multipliers API

XPrisonMultipliersAPI multipliers = api.getMultipliersApi();

// Global multipliers (server-wide, per currency)
GlobalMultiplier g = multipliers.getGlobalMultiplier(currencyName);
multipliers.setGlobalMultiplier(currencyName, value, duration, unit);
multipliers.addGlobalMultiplier(currencyName, value, duration, unit);  // extends if active
multipliers.resetGlobalMultiplier(currencyName);

// Player multipliers (per player, per currency)
PlayerMultiplier p = multipliers.getPlayerMultiplier(player, currencyName);
multipliers.setPlayerMultiplier(player, currencyName, value, duration, unit);

// Rank multipliers (assigned via permission node xprison.multiplier.<rank>)
RankMultiplier r = multipliers.getRankMultiplier(player, currencyName);

Mines API

XPrisonMinesAPI mines = api.getMinesApi();

List<Mine> allMines = mines.getMines();
Mine mine = mines.getMine(name);

// Mine interface — key methods
String name = mine.getName();
World world = mine.getWorld();
int filled = mine.getFilledBlocks();
int total = mine.getTotalBlocks();
double pct = mine.getPercentageFull();            // 0.0 – 1.0
int players = mine.getPlayerCount();

// Added for Dashboard / addon support:
Map<String, Integer> effects = mine.getEffects();         // uppercase effect name → amplifier
String resetType = mine.getResetTypeName();               // "INSTANT" or "GRADUAL"

// Block palette
BlockPalette palette = mine.getBlockPalette();
List<MineBlock> blocks = palette.getBlocks();
palette.setPaletteByIds(Map<String, Double> blockIdToPercentage);  // save and apply new palette

mine.reset();

Auto-Sell API

XPrisonAutoSellAPI autoSell = api.getAutoSellApi();

// Global prices
Map<String, Double> global = autoSell.getGlobalPrices();
autoSell.addSellPrice(MineBlock block, double price);
autoSell.removeSellPrice(MineBlock block);

// Sell regions (per WorldGuard region)
List<SellRegion> regions = autoSell.getSellRegions();
SellRegion region = regions.get(i);
String id = region.getId();
World world = region.getWorld();
String permission = region.getRequiredPermission();
Map<String, Double> prices = region.getPrices();

autoSell.addRegionSellPrice(String regionId, MineBlock block, double price);
autoSell.removeRegionSellPrice(String regionId, MineBlock block);

// 1.9 — exact-precision pricing. Prefer this when multiplying one type's price by a
// large count (an area enchant prices per block *type* and multiplies by how many were
// broken), where a double would lose precision on OP-scale servers.
BigDecimal exact = autoSell.getSellPriceForBlockExact(mineBlock);

Events

Listen to X-Prison events like any Bukkit event. All events are in the dev.drawethree.xprison.api package hierarchy.

Enchant Events

Event Cancellable Description
XPrisonEnchantPreTriggerEvent Yes Fired before a chance-based enchant rolls to trigger. Exposes the enchantment, getLevel() and getChanceToTrigger() (mutable) — adjust the proc chance or cancel the trigger. Also fired for reward-multiplier enchants (Token/Gem Merchant).
XPrisonEnchantTriggerEvent No Fired when an enchant successfully triggers. Exposes the enchantment and getLevel().
XPrisonPlayerEnchantEvent Yes Fired when a player buys enchant levels. Exposes getTokenCostExact() (BigDecimal, mutable via setTokenCostExact) and getLevel(). getTokenCost()/setTokenCost() remain as a saturating long view. Cancelling prevents the purchase.
XPrisonEnchantDisenchantEvent Yes Fired when a player refunds enchant levels. Exposes the enchantment, getCurrentLevel(), getLevelsRemoved(), isAdmin() and getRefundAmountExact() (BigDecimal, mutable via setRefundAmountExact). getRefundAmount()/setRefundAmount() remain as a saturating long view. Cancelling prevents the disenchant.
XPrisonEnchantPrestigeEvent Yes Fired when a player prestiges an enchant. Exposes getOldPrestige(), getNewPrestige() (mutable), and the enchantment. Cancelling prevents the prestige.
XPrisonEnchantRegisterEvent / XPrisonEnchantUnregisterEvent No Fired when an enchant is registered/unregistered in the repository (e.g. by an addon).
PickaxeSoulbindEvent Yes Fired when a pickaxe is soulbound to a player (including automatic bind-on-first-hold). Exposes getPlayer() (new owner) and getItemStack(). Cancelling prevents the bind.
PickaxeUnsoulbindEvent Yes Fired when a pickaxe's soulbind is cleared (e.g. /unsoulbind). Exposes getPreviousOwner() (nullable) and getItemStack(). Cancelling keeps the soulbind.

Progression Events

Event Cancellable Description
XPrisonRankupEvent Yes Player ranks up
XPrisonPrestigeEvent Yes Player prestiges
XPrisonRebirthEvent Yes Player rebirths

Block Events

Event Cancellable Description
XPrisonBlockBreakEvent No Fired for every block broken by an X-Prison pickaxe
XPrisonBulkBlockBreakEvent Yes (1.9) Fired once for a break too large to enumerate block-by-block — notably a whole packet ("virtual") mine, whose blocks have no real Block handles. Carries a Map<MineBlock, Long> of type → count plus getTotalBlocks(), so consumers scale in O(distinct block types) instead of O(blocks). Listen to this in addition to XPrisonBlockBreakEvent if your plugin tracks mined volume.

Gang Events

Event Cancellable Description
GangCreateEvent Yes A gang is created. Exposes getGangLeader() and getGang().
GangDisbandEvent Yes A gang is disbanded. Exposes getGang().
GangJoinEvent / GangLeaveEvent Yes A player joins/leaves a gang (GangLeaveEvent also covers kicks via getLeaveReason()).
GangInviteEvent Yes A player is invited to a gang. Exposes getInviter(), getPlayer() (the invited) and getGang().
GangRenameEvent Yes A gang is renamed. Exposes getGang(), getOldName(), getNewName() (mutable) and getWhoRenamed().
GangValueChangeEvent Yes A gang's value changes (admin modify/set/add). Exposes getGang(), getOldValue() and getNewValue() (mutable).
GangOwnershipTransferEvent Yes Gang ownership is transferred. Exposes getGang(), getOldOwner() and getNewOwner().

Mine Events

Event Cancellable Description
MineCreateEvent / MineDeleteEvent Yes A mine is created/deleted.
MineRenameEvent Yes A mine is renamed. Exposes getOldName() / getNewName().
MinePreResetEvent / MinePostResetEvent Pre only Fired before/after a mine resets.
MineTeleportEvent Yes A player is about to be teleported into a mine. Exposes getPlayer() and getMine().
MineRedefineEvent Yes A mine's region (bounds) is about to be redefined from a new selection. Exposes getPlayer() and getMine().

Multiplier Events

Event Cancellable Description
PlayerMultiplierReceiveEvent No A player receives a personal multiplier.
PlayerMultiplierExpireEvent No A player's multiplier expires.
PlayerMultiplierResetEvent Yes A player's personal multiplier is reset. Exposes getPlayer(), getCurrency() and getPreviousMultiplier().
GlobalMultiplierSetEvent Yes A server-wide multiplier is set. Exposes getCurrency(), getMultiplier() (mutable), getTimeUnit() and getDuration().
GlobalMultiplierResetEvent Yes A server-wide multiplier is reset. Exposes getCurrency() and getPreviousMultiplier().

Auto-Sell Events

Event Cancellable Description
XPrisonAutoSellEvent / XPrisonSellAllEvent Yes Items are auto-sold on mine / via /sellall. Exposes the mutable itemsToSell map and the sell region. Since 2026.2.8.0 the map is Map<AutoSellItemStack, BigDecimal> (was Double) for exact OP-scale prices — recompile addons that listen to these events.
AutoSellToggleEvent Yes A player's AutoSell preference is about to change. Exposes getPlayer() and isNewState() (true = enabled).

Currency Events

Event Cancellable Description
PlayerCurrencyReceiveEvent Yes A player receives currency. Exposes the cause and getAmount() (mutable).
PlayerCurrencyLoseEvent No A player loses currency. Exposes the cause and getAmount() (mutable).
PlayerCurrencyBalanceSetEvent Yes A player's balance is set. Exposes getOldAmount() and getNewAmount() (mutable).
PlayerCurrencyPayEvent Yes A player pays currency to another player (single transaction). Exposes getSender(), getReceiver(), getCurrency() and getAmount() (mutable). Cancelling blocks the whole pay.

Bomb Events

Event Cancellable Description
BombThrowEvent Yes A player throws a bomb, before its explosion timer starts. Exposes getPlayer(), getBomb() and getLocation().
BombExplodeEvent Yes A bomb explodes. Listeners approve affected blocks via addAffectedBlocks(...).
BombGiveEvent Yes A player is given bomb item(s). Exposes getPlayer(), getBomb() and getAmount().

Battle Pass / Quests / Daily Rewards Events

These modules fire their events via Bukkit's callEvent.

Event Cancellable Description
BattlePassXpGainEvent Yes A player gains Battle Pass XP. Exposes the XpSource and getAmount() (mutable).
BattlePassTierUpEvent No A player advances one or more tiers.
BattlePassRewardClaimEvent Yes A player is about to claim a tier reward. Exposes getPlayer(), getTier() and getTrack().
BattlePassPremiumChangeEvent No A player's premium status changes. Exposes getUuid() and isPremium(). May fire off the main thread.
BattlePassSeasonResetEvent No A new season starts. Exposes the old/new season ids.
QuestCompleteEvent / QuestClaimEvent / QuestAssignEvent No A quest is completed / claimed / assigned.
PlayerDailyRewardClaimEvent No A daily reward is claimed. Exposes getStreak() and getCycleDay().

PrestigeableEnchant Interface

Enchants that support prestiging implement dev.drawethree.xprison.api.enchants.model.PrestigeableEnchant:

public interface PrestigeableEnchant {
    boolean isPrestigeEnabled();
    int getMaxPrestige();
    double getMultiplierPerPrestige();
    long getRequiredActivations(int currentPrestige);
}

Use this to check whether an enchant supports prestiging and read its configuration at runtime.


GUI Pages

Since X-Prison 2026.3.3.0. The enchant menus are paginated, and two interfaces gained a page accessor to go with the existing slot accessor:

// dev.drawethree.xprison.api.enchants.model.XPrisonEnchantmentGuiProperties
int getGuiSlot();
default int getGuiPage() { return 1; }   // 1-based

// dev.drawethree.xprison.api.enchants.model.RefundableEnchant
int getRefundGuiSlot();
default int getRefundGuiPage() { return 1; }   // 1-based

Both are default methods, so existing addons keep compiling and running unchanged — they simply report page 1. If you extend XPrisonEnchantmentBase, both values are read from your enchant's JSON for free: gui.page and refund.guiPage, each optional and defaulting to 1.

Pages are 1-based. You do not need to guarantee a free slot: if the slot or page you ask for is already taken, or falls outside the menu's configured content region, the core moves your enchant to the next free slot and onto a later page if required. An out-of-range slot can no longer throw and break the menu.

refund.guiSlot is now optional too (defaulting to -1), so an enchant JSON that omits it still loads instead of failing outright.


Notes

  • The API jar is provided scope — do not shade it into your plugin or addon.
  • Check the X-PrisonAPI GitHub repository for the most up-to-date interface definitions.
  • For questions about the API, open a ticket in the Discord server.

XPrison Logo

General

Modules

Default Configs

Enchant Configs — Passive

Enchant Configs — Currency Rewards

Enchant Configs — Key & Item Rewards

Enchant Configs — Area of Effect

Enchant Configs — Multipliers

Enchant Configs — Templates

Enchant Configs — Addons

Addons

Support

For Developers

Others

Clone this wiki locally