Extending PulsePrison
Besides reading and changing data, a plugin can add content that server owners then use from YAML: action types, currency providers, enchants and stat sources.
| What | How | Status |
|---|---|---|
| Custom actions | actions().register | Public API |
| Currencies from your plugin | currencies().registerProviderType | Public API |
| Java enchants | pickaxe().registerEnchant | Public API, the base class is internal |
| Stat sources | BonusService.register | Advanced, internal |
Custom actions
Register a type and it works in every action list: level rewards, milestones, masteries, boosters, abilities, lucky blocks, AFK blocks, custom enchants and menus.
prison.actions().register("crate", (player, argument) -> {
String[] parts = argument.split(" ");
crates.giveKey(player, parts[0], parts.length > 1 ? Integer.parseInt(parts[1]) : 1);
});The server owner uses it like this:
rewards:
levels:
"25":
- "[crate] legendary 2"
- "[chance=10] [crate] mythic"argumentarrives with{player}, the system placeholders and PlaceholderAPI placeholders already replaced.- The
[chance],[delay]and[permission]prefixes are resolved by PulsePrison before calling you. - The handler runs on the main thread.
- Register in
onEnable. A type with the same name as another replaces it.
Currencies from your plugin
Add a new provider: value for currencies.yml. Any server can then use your currency in costs, rewards, /balance, /pay and placeholders without configuring commands.
prison.currencies().registerProviderType("myaddon", (currencyId, section) -> {
String account = section.getString("account", currencyId);
return new CurrencyApi.ExternalCurrency() {
@Override
public double balance(UUID player) {
return bank.get(player, account);
}
@Override
public boolean add(UUID player, double amount) {
return bank.deposit(player, account, amount);
}
@Override
public boolean take(UUID player, double amount) {
return bank.withdraw(player, account, amount);
}
@Override
public boolean set(UUID player, double amount) {
return bank.set(player, account, amount);
}
@Override
public boolean available() {
return bank.isConnected();
}
};
});# the server's currencies.yml
currencies:
souls:
provider: myaddon
account: souls # any extra option arrives in "section"
name: "Souls"
format: "&5{amount} {name}"- When the type is registered, PulsePrison reloads
currencies.yml, so currencies that already used it activate even though your plugin loads later. - Return
nullfrom the factory if the section is misconfigured: that currency isn't loaded and the console reports it. - If your source can disconnect, make
takereturnfalsemeanwhile: costs are not charged.
Java enchants
For effects YAML can't express, extend BaseEnchant. The enchant shows in the pickaxe menu, is bought with its currency, respects conflicts and requirements, earns mastery and runs while mining.
BaseEnchant,EnchantContextandEnchantLoader.EnchantParamslive outside theapipackage: they may change in a major version.
Parameters:
import dev.aeros.pulseprison.enchants.base.EnchantRarity;
import dev.aeros.pulseprison.enchants.base.EnchantTrigger;
import dev.aeros.pulseprison.enchants.base.EnchantType;
import dev.aeros.pulseprison.enchants.registry.EnchantLoader.EnchantParams;
public final class SoulParams {
public static EnchantParams create() {
EnchantParams p = new EnchantParams();
p.id = "soul_harvest";
p.displayName = "&5☠ Soul Harvest";
p.type = EnchantType.PICKAXE;
p.trigger = EnchantTrigger.POST_BREAK;
p.rarity = EnchantRarity.EPIC;
p.maxLevel = 50;
p.baseCost = 100000;
p.costMultiplier = 1.08;
p.currency = "tokens";
p.requiredPickaxeLevel = 20;
p.guiSlot = 43;
p.guiItem = "WITHER_SKELETON_SKULL";
p.levelValues = Map.of(1, 1.0, 50, 25.0);
p.levelChances = Map.of(1, 0.5, 50, 5.0);
p.descriptionEN = List.of("&7Harvest souls while mining.", "&7Level {level}: &f{value} souls ({chance}%)");
p.descriptionES = List.of("&7Cosecha almas al minar.", "&7Nivel {level}: &f{value} almas ({chance}%)");
return p;
}
}Enchant: BaseEnchant has a protected constructor with the EnchantParams fields in order, the same pattern the built-in enchants use.
import dev.aeros.pulseprison.api.PulsePrisonProvider;
import dev.aeros.pulseprison.enchants.base.BaseEnchant;
import dev.aeros.pulseprison.enchants.base.EnchantContext;
import dev.aeros.pulseprison.enchants.registry.EnchantLoader.EnchantParams;
import dev.aeros.pulseprison.enchants.utils.ChanceCalculator;
public final class SoulHarvestEnchant extends BaseEnchant {
public SoulHarvestEnchant(EnchantParams p) {
super(p.id, p.displayName, p.type, p.trigger, p.rarity,
p.maxLevel, p.baseCost, p.costMultiplier, p.currency,
p.requiredPickaxeLevel, p.conflictsWith, p.requires,
p.guiSlot, p.guiItem, p.levelValues, p.levelChances,
p.descriptionES, p.descriptionEN);
}
@Override
public boolean activate(EnchantContext context) {
int level = context.getEnchantLevel();
if (!ChanceCalculator.roll(getLevelChance(level))) {
return false;
}
double souls = getLevelValue(level);
PulsePrisonProvider.get().currencies().give(context.getPlayer().getUniqueId(), "souls", souls);
return true;
}
}Registration:
@Override
public void onEnable() {
prison = PulsePrisonProvider.get();
prison.pickaxe().registerEnchant(new SoulHarvestEnchant(SoulParams.create()));
}
@Override
public void onDisable() {
if (PulsePrisonProvider.isAvailable()) {
PulsePrisonProvider.get().pickaxe().unregisterEnchant("soul_harvest");
}
}What to do in activate
- Return
trueonly when the enchant did something: it counts for masteries and activation messages. - Use
ChanceCalculator.roll(getLevelChance(level)): it applies theenchant-chancestat and the skill tree. getLevelValueandgetLevelChancealready interpolate between the levels inlevelValuesandlevelChances.- Paying: the enchant pays its own rewards. For tokens and gems, pay the base amount with
economy().addTokensoraddGemsand report it withcontext.addTokenBonusoraddGemBonus: PulsePrison pays the extra from bonuses (skill tree, pet, stats) on top and shows it in the action bar. For money, pay and report withcontext.addMoneyBonus. - Other currencies:
currencies().give. - Pickaxe experience:
context.multiplyExp(1.5). PRE_BREAKruns before the block is paid andPASSIVEon every block without its own chance.
An enchant registered from Java doesn't read enchants.yml: its values are those in EnchantParams. If you want server owners to change them, read them from your plugin's own config file.
Stat sources
Advanced. An addon can add to the stats with the same interface attributes, crystals and armor use. The bonuses show in /stats and affect everything that uses that stat.
BonusService.BonusProvider provider = (player, sink) -> {
int prestige = myRanks.getLevel(player);
sink.add("sell", prestige * 0.01);
sink.add("enchant-chance:soul_harvest", prestige * 0.02);
};
PulsePrison.getInstance().getBonusService().register(provider);contributeis called often: PulsePrison caches the total per player for one second. Keep it fast and free of database queries.- If your values change suddenly, call
getBonusService().invalidate(uuid). - Remove it in
onDisablewithunregister(provider).
dev.aeros.pulseprison.bonus.BonusService is internal: it may change between versions.
Current limits
- Effect types for
custom-enchants/: YAML enchants load when PulsePrison starts, before addons, so an addon can't add new effect types yet. Use custom actions inside theactionseffect, or a Java enchant. - Menus: an addon can't add actions or placeholders to PulsePrison menus. It can add buttons that run its commands or its custom actions.
- Events: the newer progression systems have no events. See What has no event.
- Artifact: there is no Maven repository; compile against the jar.