Skip to content
ShadowMC

Configuration

ShadowBattlePass

A season, a hundred and forty missions, and a reason to come back tomorrow.

Premium v1.0.4

Where the files are

plugins/ShadowBattlePass/

The plugin creates them on first start and updates them by itself on new versions, keeping your changes.

Applying changes

/bpa reload

Reloads the files without restarting the server.

What’s inside

33 files · 580 settings

Start with: config.yml

Settings

The files that decide how the plugin behaves. Start here.

config.yml

General settings
103 settings
plugins/ShadowBattlePass/config.yml
File notes

On update your files (this one, seasons/, quests/, lang/, menus/ and the others below) are moved to the new version automatically: every value you changed or added stays, new options arrive with their comments, and what you deleted stays deleted. The old copies are in backup/<date>/; if something needs your attention you will find PORTING-<version>.txt in the plugin folder.

A wrong value blocks nothing: it is replaced with the factory one and reported in console at start-up, with the path and expected value.

Where things live:

config.yml        this file: storage, rules, notifications, hooks
seasons/*.yml     the seasons: tiers, tracks, rewards
quests/*.yml      the quests, one file per pool (daily, weekly, ...)
rewards.yml       reusable reward bundles
boosters.yml      scheduled multipliers (weekend, happy hour)
shop.yml          the shop you pay with pass coins
challenges.yml    the server-wide challenges
lang/*.yml        the messages
menus/*.yml       the layout of every screen
config-version4
storage7 keys
storage.mysql7 keys
enabledfalse

false = local H2 database inside plugins/ShadowBattlePass/data/. Nothing to install: the plugin works the moment it is copied in. Put your credentials here and restart: the plugin creates every table on its own, and nothing in the local file is touched.

host127.0.0.1
port3306
databaseshadowbattlepass
usernameroot
password""
parameters"useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=utf8"
storage
table-prefix"bp_"

Table prefix. Change it and the plugin creates new tables and starts from scratch: useful for two servers sharing one database.

fallback-to-localtrue

MySQL configured but unreachable: with true the plugin says so in console and falls back to the local file, so the server still starts.

flush-interval"30s"

How often changed progress reaches the database. On shutdown a blocking save always happens, whatever this value says.

storage.pool5 keys
maximum-pool-size10
minimum-idle2
connection-timeout-ms10000
idle-timeout-ms600000
max-lifetime-ms1800000
storage.h21 key
auto-serverfalse

Lets another process - a web panel, say - open the same H2 file while the server is running. Rarely needed.

Several servers on one database

Only needed if you run a network (lobby + survival + ...) sharing one MySQL. The servers pass news to each other through the database: no ports to open, nothing to install on the proxy.

Keep reading

What travels is a NOTICE, never a value: a message says "the season changed", not "the season is now season-2". Whoever gets it goes and re-reads the truth from the database, so two servers that change something in the same second converge instead of overwriting.

A player's pass does not need to travel: you are on one server at a time, the pass loads on join and is written on quit, and two servers never hold the same player.

storage.sync5 keys
modeAUTO

AUTO = on when you use MySQL, off on the local database. ON | OFF to decide it yourself.

primarytrue

IMPORTANT: across a network, EXACTLY ONE server may have primary: true. That is the one that starts and ends seasons and fires the scheduled challenges. The others read and display.

server-idauto

A name for this server. "auto" uses hostname + port. Two servers must never share one.

poll-interval"3s"

How often to read what the others have done.

keep-messages"1h"

How long to keep messages already read before deleting them.

Messages and languages

messages2 keys
default-locale"en_US"

The language used when the player's own is not in lang/, and by the console. Ships with en_US and it_IT: set "it_IT" for an Italian server.

follow-client-localetrue

true = every player sees messages in their client's language. They can still pick one with /bp lang.

Time

This decides when quests rotate. It applies to everyone at the same instant: two players online on the same day get the same daily quests, and the reset is not "24 hours after your first login".

time3 keys
zone"system"

"system" uses the server's zone. Or name one, e.g. "Europe/Rome".

daily-reset"00:00"

What time the daily quests change. Before this hour the day has not turned yet, so somebody playing at 3 in the morning does not lose a whole cycle.

week-startsMONDAY

Which day the week starts on, for the weekly quests.

Where and how experience is earned

worlds2 keys
only-in[ ]

If this list is NOT empty, the pass works ONLY in these worlds.

never-in[lobby]

Otherwise it works everywhere except here.

earning3 keys
conditions[ ]

Requirements that must hold for experience to be counted. Same syntax as the quest conditions (see docs/07).

- "gamemode: survival, adventure"
- "not-world: creative"
daily-xp-cap0

Daily experience ceiling per player. 0 = no ceiling. Not a punishment: it is what stops an afk farm from closing a ninety day season in one afternoon.

earning.xp-sources7 keys

Passive experience: how much every single action gives, even with no quest active. Leave empty for a pass that advances ONLY through quests, which is the commonest and the best balanced choice.

For amount-based actions (damage, money, distance) the number is per unit, with an internal cap so one hit cannot produce absurd numbers.

Full list of types: docs/05-OBIETTIVI.md

ORE_MINE1
MOB_KILL1
ITEM_FISH2
CROP_HARVEST1
ADVANCEMENT25
RAID_WIN100
BOSS_KILL250

Anti abuse

This is what keeps a "break 500 ores" quest about mining instead of becoming "place and break the same block 500 times".

anti-abuse6 keys
ignore-placed-blockstrue

A block placed by a player does not count when it is broken. The memory lives in the chunk itself, so it survives a restart.

placed-blocks-per-chunk4096

How many placed blocks to remember per chunk. Past this number the oldest ones are forgotten.

ignore-spawner-mobstrue

Mobs out of a spawner or an egg do not count for quests that ask you to kill. Individual quests may override this.

ignore-afktrue

Somebody who has not moved the mouse for a while is not playing: their actions do not count until they come back.

afk-after"5m"
max-events-per-second64

Ceiling of events per second per player. Not an anticheat: a safety net against machines that break thousands of blocks a second. 0 = no limit.

Claiming rewards

claiming5 keys
auto-claim-tiersfalse
true = rewards arrive on their own as soon as you gain a tier.
false = the player clicks, which feels better and is far commoner.
auto-claim-questsfalse

Same for finished quests.

auto-claim-on-season-endtrue

At the end of a season everything left unclaimed is handed over anyway. Leave this on: nobody should lose a reward they had earned just because they were offline.

allow-tier-dropfalse

If you lower the curve mid-season, true recalculates downwards for anyone already past it. Normally you do not want that.

mailboxtrue

Mailbox: an item that does not fit the inventory is not dropped and does not vanish, it waits there until there is room. Switch it off and a reward that does not fit is refused.

Notifications

The delicate part of a battle pass. Too little and progress is invisible; too much and the chat is unreadable after a minute.

notifications8 keys
join-summarytrue

A summary in chat when a player joins.

notifications.xp2 keys
enabledtrue

Experience gained shows in the action bar, never in chat.

every"1s"

Gains are added up and shown at most once every:

notifications.boss-bar2 keys
enabledtrue

A bar under the crosshair while experience moves.

ticks60

How many ticks it stays visible after the last gain.

notifications.bar3 keys
length20

The characters of the progress bar, used everywhere.

full"█"
empty"░"
notifications.tier3 keys
titletrue

A title on screen when a tier is gained.

broadcast-every10

Announce to everyone in chat every N tiers. 0 = never.

sound"entity.player.levelup 0.7 1.2"
notifications.quest4 keys
titletrue
sound"entity.experience_orb.pickup 0.8 1.4"
progresstrue

Progress notices during a long quest.

progress-steps4

In how many steps: 4 means at 25%, 50%, 75%. 0 = never.

notifications.challenge-bar2 keys
enabledtrue
format"<gradient:#B388FF:#7C4DFF><challenge></gradient> <white><progress>/<goal> <gray>(<time>)"
notifications.announce3 keys
season-starttrue
track-unlocktrue
quest-resettrue

Login streaks

The cheapest and most honest way to bring somebody back tomorrow.

streaks3 keys
enabledtrue
grace-days1

How many days you may miss without losing the streak. Losing thirty days in a row to one disconnection makes people stop playing, not play more.

streaks.rewards5 keys

The table may have gaps: whoever reaches day 12 gets the day 7 prize, not nothing.

1["coins: 10"]
3["coins: 25", "xp: 100"]
7
["coins: 75", "xp: 500", "message: <gradient:#FFD54F:#FF8F00>A whole week in a row!</gradient>"]+1
14["coins: 150", "xp: 1000"]
30
["coins: 500", "xp: 3000", "tier: 1", "broadcast: &6<player> has played 30 days in a row!"]+1

Catch-up for whoever falls behind

A season is fun for as long as catching up looks possible. Whoever joins late, or comes back after two weeks, earns faster - up to a ceiling, and only until they have caught the others.

catch-up3 keys
enabledtrue
behind-tiers10

How many tiers below average you must be for it to kick in.

max-multiplier2.0

The most it can ever multiply by.

Shared team pass

On a skyblock server the unit that matters is the island, not the account. With this on every member feeds one shared bar and every member may claim the rewards of the tiers reached - once each, so four players do not turn one tier into four copies of the same prize.

Needs ShadowSkyBlock. Without it the feature simply never switches on.

team3 keys
enabledfalse
share0.25

How much of what a member earns reaches the shared bar. 0.25 = a quarter.

track"free"

Which track of the season holds the team prizes.

Gifting the pass

Buying a track for a friend. It is the most requested feature of any battle pass and the one nobody implements: it costs the server nothing - the money leaves the economy either way - and it turns the premium track into something you can give for a birthday, which sells far more copies than any discount.

gifting3 keys
enabledtrue
markup0.0

Surcharge, as a fraction. 0 = none, 0.1 = ten per cent more. Zero by default: gifting should be encouraged, not taxed.

announcetrue

Announce the gift to the other players.

Leaderboard

leaderboard3 keys
size100

How many places to keep in memory.

refresh"2m"

How often it is recalculated.

hologram-size10

How many places the floating leaderboard shows. Place it with /bpa hologram place <board>, standing where you want it. Needs ShadowEssentials for the holograms.

Screens

gui2 keys
input-timeout"60s"

How long an administrator has to answer a question the editor asks in chat before it is cancelled.

refresh-ticks20

Housekeeping

housekeeping3 keys
history-days60

How many days to keep the history of rewards given. 0 = forever.

mailbox-days30

How many days to keep parcels already delivered.

quest-days30

How many days to keep progress of cycles closed and claimed.

Integrations

All optional. A missing plugin degrades one feature and never stops a start-up.

integrations3 keys
suitetrue

Announce ourselves to ShadowEssentials, so /se suite lists us.

integrations.permissions3 keys
grant-command""

The command used to grant a permission as a reward. Leave empty to let the plugin choose: it recognises ShadowPermissions, LuckPerms, PermissionsEx and GroupManager. Placeholders: <player> <node> <duration>

grant-timed-command""
revoke-command""
integrations.crates1 key
give-command""

The command used to give crate keys as a reward. Empty = recognises ShadowCrazyChest, CrazyCrates, ExcellentCrates. Placeholders: <player> <crate> <amount>

Diagnostics

debug1 key
log-sqlfalse

Writes every query to console. Only to chase a problem down: on a real server it fills the log in minutes.

bStats (anonymous usage statistics, https://bStats.org)

What is sent, every 30 minutes: a random server id (serverUuid, not derived from anything), the plugin version, the Minecraft/Paper and Java versions, the operating system and core count, the online mode, how many players are online (a number only) and the licence tier (free/premium/creator). NO player data: no names, no UUIDs, no addresses, no progress. Nothing is sent while the plugin waits for a licence.

Keep reading

false switches off this plugin only. enabled: false in plugins/bStats/config.yml switches off every bStats plugin on the server at once.

metrics1 key
enabledtrue

The licence

Without a key the plugin runs the free edition. It never switches itself off and never deletes anything: what exceeds the limits is not loaded but stays on disk, intact, and comes back when a key arrives.

Keep reading

The key does NOT go here. It lives in license.yml, a separate file on purpose: a support request saying "send me your config" must not arrive with somebody's licence inside it. The field below exists only for people who generate the config from a deploy system.

license3 keys

THE KEY LIVES IN license.yml (same folder as this file), or in the central plugins/ShadowControl/licenses.yml for the whole suite. The 'key' below is only a fallback for deploy systems that generate config.yml: leave it empty.

The key does NOT live here: it lives in license.yml, in this folder, written at first start (or in plugins/ShadowControl/licenses.yml for the whole suite). Left empty: use /bpa license activate <key>, or license.yml.

activation-url'https://shadow-licences.riccardobotins.workers.dev'

The BASE address of the activation service. The plugin appends /claim and /check itself. Empty means no automatic activation: keys are pasted by hand and everything else keeps working.

grace-days7

How many days of grace when a licence expires or the addresses do not match. Seven, because a renewal that arrives on Monday morning must not cost anybody a weekend, and because an address that does not match is far more often a NAT than a thief.

resolve-public-iptrue

Ask the licence service (activation-url + /ip) what this server's public address is: it answers with the address it sees, and nothing about the server is sent. Only needed to match a licence binding behind NAT. Switch it off if you prefer the plugin to talk to nobody. Only when a licence is installed: a server without a key asks nobody. false = never ask.

boosters.yml

Boosters
21 settings
plugins/ShadowBattlePass/boosters.yml
File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/boosters.yml

Copy it over this file to use it (copiala al posto di questo file).

The experience multipliers.

They come from four different places and all end up as one number:

1. PERMISSION    shadowbattlepass.booster.150  ->  x1.5
   The number is a percentage: 200 doubles it.
2. SCHEDULE      the windows defined below (weekend, happy hour)
3. PERSONAL      bought in the shop or handed out as a reward
4. GLOBAL        started by an administrator for the whole server

How they combine is a decision, not an accident: see "stacking".

config-version2
stackingfalse

false = the best one wins. true = they multiply together.

Leave this on false unless you know exactly what you are doing: two boosters that stack are the quickest way to have a season finished over a single weekend.

max-multiplier5.0

The hard ceiling, whatever the rules above end up saying.

Scheduled windows

Each one switches itself on during the days and hours named here. Drop "days" to make it apply every day. Drop "from" and "to" to make it apply all day. Add "permission" to limit it to one group.

schedules4 keys
schedules.weekend4 keys
enabledtrue
display"&6Weekend x1.5"
multiplier1.5
days[SATURDAY, SUNDAY]
schedules.happy-hour5 keys
enabledtrue
display"&bHappy hour x2"
multiplier2.0
from"20:00"
to"22:00"
schedules.night-shift5 keys

A window that crosses midnight works fine: 22:00 -> 02:00.

enabledfalse
display"&9Night shift x1.25"
multiplier1.25
from"22:00"
to"02:00"
schedules.vip4 keys

Only for whoever holds a permission: the first days of a rank, say.

enabledfalse
display"&dVIP bonus"
multiplier1.25
permission"shadowbattlepass.booster.vip"

challenges.yml

Challenges
52 settings
plugins/ShadowBattlePass/challenges.yml
File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/challenges.yml

Copy it over this file to use it (copiala al posto di questo file).

Something the whole server does together.

"Kill fifty thousand zombies by Sunday night." One bar, everybody pushing it, a prize when it fills. This is what turns a battle pass from a personal to-do list into something people talk about in chat - and no other plugin of this kind has it.

Everyone's contribution is recorded, so the prize can be fair: whoever helped gets something, whoever helped most gets more, and whoever turned up in the last minute does not take home the same prize as the person who carried it.

Objectives use the same types as quests: docs/05-OBIETTIVI.md

config-version2
enabledtrue
challenges3 keys

Saturday night, two hours, everybody killing undead

challenges.purge13 keys
enabledtrue
display"<gradient:#EF5350:#B71C1C><bold>The Purge</bold></gradient>"
description
["&7The whole server against the undead.", "&7Two hours to take out ten thousand."]+1
iconZOMBIE_HEAD
challenges.purge.objective3 keys
typeMOB_KILL
target
[ZOMBIE, SKELETON, HUSK, DROWNED, STRAY, ZOMBIE_VILLAGER]+3
no-spawner-mobstrue

Spawner mobs do not count: that would be a lever, not a battle.

challenges.purge
goal10000
duration2h
auto-starttrue

When it starts by itself. Drop "auto-start" to run it by hand only, with /bpa challenge start purge.

periodWEEKLY
days[SATURDAY]
at"21:00"
xp-per-contribution2

Experience given on every contribution, while the challenge runs.

challenges.purge.rewards4 keys
min-contribution25

How much you must have done to count as a participant.

participant["coins: 100", "money: 10000"]
challenges.purge.rewards.top3 keys

First place takes the "1" prize, second and third take the "3" one, fourth to tenth take the "10" one.

1
["bundle: epic-crate", "broadcast: &6<player> led the Purge!"]+1
3["bundle: rare-crate"]
10["bundle: common-crate"]
challenges.purge.rewards
everyone["booster: 1.5 1h"]

Given to everybody online when the challenge is won, even to those who never lifted a finger. It makes the event land for everyone.

Sunday afternoon, everybody digging

challenges.big-dig13 keys
enabledtrue
display"<gradient:#4FC3F7:#0288D1><bold>The Great Dig</bold></gradient>"
description
["&7A hundred thousand blocks of stone.", "&7Sounds like a lot. It is not, if everybody pitches in."]+1
iconDIAMOND_PICKAXE
challenges.big-dig.objective3 keys
typeBLOCK_BREAK
target
[STONE, DEEPSLATE, ANDESITE, DIORITE, GRANITE, TUFF]+3
no-placed-blockstrue
challenges.big-dig
goal100000
duration3h
auto-starttrue
periodWEEKLY
days[SUNDAY]
at"16:00"
xp-per-contribution1
challenges.big-dig.rewards3 keys
min-contribution200
participant["coins: 75"]
challenges.big-dig.rewards.top2 keys
1["bundle: epic-crate"]
5["bundle: rare-crate"]

By hand only: the one to bring out for an event

challenges.big-catch10 keys
enabledtrue
display"<gradient:#80DEEA:#00838F><bold>The Big Catch</bold></gradient>"
description["&7A thousand fish. Bring patience."]
iconFISHING_ROD
challenges.big-catch.objective1 key
typeITEM_FISH
challenges.big-catch
goal1000
duration1h
auto-startfalse
xp-per-contribution5
challenges.big-catch.rewards3 keys
min-contribution5
participant["coins: 150"]
challenges.big-catch.rewards.top1 key
1["item: TRIDENT 1", "coins: 500"]

rewards.yml

Rewards
15 settings
plugins/ShadowBattlePass/rewards.yml
File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/rewards.yml

Copy it over this file to use it (copiala al posto di questo file).

Named reward bundles.

Write a bundle here once, then call it from wherever you like - a season tier, a quest, a shop entry, a challenge - with one line:

- "bundle: epic-crate"

That is the whole point: if you decide the epic crate should give twice the money, you change it in one place and it changes in all twenty tiers that use it, without opening a single season file.

A bundle can contain anything, bundles included.

The full reward syntax is in docs/06-RICOMPENSE.md.

config-version2
bundles14 keys

Crates

bundles
common-crate
["coins: 25", "item: EXPERIENCE_BOTTLE 8", "money: 2500"]+1
rare-crate
["coins: 75", "item: DIAMOND 3", "money: 10000", "sound: entity.player.levelup 0.8 1.4"]+1
epic-crate
["coins: 200", "money: 50000", { type: RANDOM, display: "&dEpic item", picks: 1, options: […] }, "firework: B388FF"]+2

Milestone moments

bundles
welcome
["coins: 25", "item: BREAD 16", "message: &7A little help to get you started."]+1
season-end
["coins: 1000", "money: 100000", "booster: 2.0 24h", "item: TOTEM_OF_UNDYING 3", "firework: FFD54F"]+3

Reusable building blocks

bundles
snack
["item: COOKED_BEEF 16", "item: GOLDEN_CARROT 8"]+1
starter-gear[{ type: ITEM, item: {…} }, "item: TORCH 64"]

Example with an item from another plugin. The line works only if that plugin is present; if it is missing, the bundle says so in console and carries on with the rest instead of breaking.

item-custom:
  - "item: oraxen:mythril_sword"
  - "item: mmoitems:SWORD:EXCALIBUR"

Kits handed out along the season

Vanilla items only: they work on a freshly installed server. Enchanted, named, and unbreakable where it matters, so a level 15 reward is not an iron pickaxe anyone can craft.

bundles
iron-kit
[{ type: ITEM, item: {…} }, { type: ITEM, item: {…} }, "item: IRON_CHESTPLATE 1", "item: COOKED_BEEF 16"]+3
diamond-kit
[{ type: ITEM, item: {…} }, { type: ITEM, item: {…} }, { type: ITEM, item: {…} }, "item: GOLDEN_APPLE 4"]+3
netherite-kit
[{ type: ITEM, item: {…} }, { type: ITEM, item: {…} }, "item: NETHERITE_INGOT 2", "item: ENCHANTED_GOLDEN_APPLE 1"]+3
builder-kit
["item: STONE_BRICKS 128", "item: OAK_PLANKS 128", "item: GLASS 64", "item: LANTERN 16", "item: SCAFFOLDING 64"]+4
farmer-kit
["item: BONE_MEAL 64", "item: WHEAT_SEEDS 32", "item: CARROT 16", "item: POTATO 16", { type: ITEM, item: {…} }, "item: WATER_BUCKET 2"]+4
mob-kit
[{ type: ITEM, item: {…} }, "item: ARROW 1", "item: SHIELD 1", "item: COOKED_PORKCHOP 16"]+2
potion-kit
["effect: speed 1200 2", "item: GOLDEN_CARROT 16", "item: TOTEM_OF_UNDYING 1", "item: ENDER_PEARL 8", "item: EXPERIENCE_BOTTLE 16"]+4

shop.yml

The shop
24 settings
plugins/ShadowBattlePass/shop.yml
File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/shop.yml

Copy it over this file to use it (copiala al posto di questo file).

What pass coins buy. Coins come from the tiers and the quests; here they go. Three shelves, and every shelf full from day one: a shop with one thing in it teaches players that coins are worthless, and after that no quest is worth doing.

Every entry:

category:        one of the ids under "categories" below
display / description / icon
price:           coins, money (needs an economy plugin), or tiers
limit-per-cycle + cycle (DAILY, WEEKLY, MONTHLY, SEASON): how many
                 times per player per cycle; left out = no limit
limit-per-player: how many times ever
conditions:      same syntax as the quests ("tier: >= 30")
rewards:         same short form as the seasons and the quests
featured:        true puts a glow on it
config-version3
enabledtrue
currency-name"Pass Coins"
categories3 keys
categories.utili4 keys
display"<green><bold>Useful</bold>"
description["&7Things you actually need."]
iconCHEST
order0
categories.progressi4 keys
display"<gold><bold>Progress</bold>"
description["&7To move up faster."]
iconLADDER
order1
categories.cosmetici4 keys
display"<light_purple><bold>Cosmetics</bold>"
description["&7Just for showing off."]
iconFIREWORK_ROCKET
order2
entries22 keys

Useful

entries.reroll9 keys
enabledtrue
categoryutili
display"&eSwap a quest"
description
["&7A daily quest you do not fancy", "&7is replaced with another one."]+1
iconCLOCK
entries.reroll.price1 key
coins50
entries.reroll
limit-per-cycle2
cycleDAILY
rewards["reroll: 1"]
21 more entries shaped like reroll: snack, golden-apples, ender-pearls, common-crate, rare-crate, epic-crate, gear, farmer-kit, totem, shulker, booster-2h, booster-6h, xp-pack, tier-skip, vanilla-xp, fireworks, fireworks-show, aura, veteran-title, champion-title, player-head

vouchers.yml

Vouchers
12 settings
plugins/ShadowBattlePass/vouchers.yml
File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/vouchers.yml

Copy it over this file to use it (copiala al posto di questo file).

Vouchers: rewards with a body.

A voucher is an item that, right-clicked, does something to the pass: unlocks a track, adds experience, hands over a bundle.

It is what a store actually needs: a product that can be given, kept, traded and used whenever the buyer feels like it, instead of a command that fires at the moment of payment even if the player is not online right then.

The item carries ONLY the voucher id, never its contents. That is the choice that makes it safe: tampering with the item cannot turn a coal voucher into a diamond one, and changing your mind means changing one file instead of chasing every copy already in circulation.

To hand one out:

/bpa voucher give <player> <id> [amount]
config-version2
vouchers6 keys

The premium pass, in item form

This is the voucher a store sells.

vouchers.premium10 keys
enabledtrue
display"<gradient:#FFD54F:#FF8F00><bold>Premium Pass</bold></gradient>"
description
["&7Unlocks the premium track of the season", "&7and hands you everything you missed, right away."]+1
vouchers.premium.item2 keys
materialPAPER
glowtrue

item-model: "shadow:voucher_premium"

vouchers.premium
unlock-trackpremium

What it does.

retroactivetrue

Also hands over the backlog of tiers already reached.

season""

Valid only for the season it was created in. Leave it empty to make it valid for any season: a premium bought for season 1 should not quietly unlock season 4 two years later, and this is how you say so.

The "/bpa voucher give" command writes the season active at creation time into it, so normally nothing is needed here.

consumetrue
broadcasttrue
stack-size1
5 more entries shaped like premium: deluxe, experience, coins, booster, veteran-crate

seasons/season-1.yml

Season: season-1
87 settings
plugins/ShadowBattlePass/seasons/season-1.yml
File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/seasons/season-1.yml

Copy it over this file to use it (copiala al posto di questo file).

ONE SEASON = ONE FILE. Copy this file, change its name and id, and you have season 2. The plugin finds them on its own: there is nothing to register anywhere.

Three ideas keep this file short even with a hundred tiers:

1. A CURVE, NOT A TABLE
   The cost of every tier comes from a formula. Only the tiers that
   break the pattern are written by hand, under "overrides".
2. PATTERNS, NOT REPETITION
   A reward can be attached to tier 7, to the range 1-10, to "*5"
   (every fifth tier) or to "default" (every tier nobody else took).
   The exact number always beats the pattern, so you can write the
   rule first and the exception after it.
3. AS MANY TRACKS AS YOU LIKE
   Free and premium are just the two you find here. You can add more:
   each one has its own permission, its own price and its own rewards
   at every tier.
config-version2
season15 keys
idseason-1
display"<gradient:#FFD54F:#FF8F00><bold>Season 1 - The Lost Lands</bold></gradient>"
description
["&7Ninety days of exploring, digging and fighting.", "&7A hundred tiers, three tracks, rewards that last.", "&7A hundred and twenty-nine quests in rotation, and two stories."]+2
season.icon2 keys
materialNETHER_STAR
glowtrue
season
enabledtrue

When it runs

You can give a start date and an end date, or just a duration. With neither, the season stays active until you start another one by hand with /bpa season start.

season
duration90d
start: "2026-09-01 00:00"
end:   "2026-11-30 23:59"
auto-start-nextfalse

When it ends it moves to the next season file on its own.

Keep reading

SEASON TWO, when you want one: copy this file to seasons/season-2.yml, change "id" to season-2 and the display to match, and set the two lines below to true / season-2. Nothing else has to be registered anywhere, and nothing in the database has to be cleared: progress rows keep the id they were written with, so season 1 stays readable for ever.

Until that file exists, leave these as they are. A season that points at a file that is not there just carries on, but the console says so every time it checks, and that noise hides real warnings.

next: season-2

The tracks

season.tracks3 keys
season.tracks.free5 keys
display"<green><bold>Free</bold>"
description["&7For everyone, always."]
iconLIME_DYE
defaulttrue
order0
season.tracks.premium6 keys
display"<gold><bold>Premium</bold>"
description
["&7The main track of the season.", "<gray>Unlock it and you get <white>right away</white> everything", "&7you had already earned."]+2
iconGOLD_INGOT
order1
permission"shadowbattlepass.premium"

Whoever has this permission owns the track: this is the node to hand out from your store (Tebex, Craftingstore, ...), from a rank, or with /bpa unlock. There is no in-game purchase, on purpose: who gets the premium track is the founder's decision, not a price tag.

unlock-retroactivetrue

Unlocking it mid-season hands over the whole backlog at once. That is what a player expects, and it is what every other plugin gets wrong.

season.tracks.deluxe6 keys
display"<light_purple><bold>Deluxe</bold>"
description["&7A third row, for those who want it all."]
iconAMETHYST_SHARD
order2
permission"shadowbattlepass.deluxe"
unlock-retroactivetrue

The tiers

THE MATHS, so that if you change something you know what you changed. Every number below is the one the files in quests/ actually produce.

Keep reading

WHAT THE LADDER COSTS

tier   1     1,000          cumulative       1,000
tier  10     1,900                          14,500
tier  25     3,400                          55,000
tier  50     8,000  (override)             174,600
tier  75     8,400                         354,600
tier 100    25,000  (override)             611,200

611,200 experience over ninety days is 6,791 a day.

WHAT THE QUESTS PAY IN, IF YOU CLEAR THEM

daily        237,449     2,638 a day   three of thirty-nine, plus
                                       the 800 for clearing all three
weekly       251,573     2,795 a day   four of the nine of that week
monthly      127,149     1,413 a day   two of fifteen
premium       98,190     1,091 a day   two of twenty-four, reserved
seasonal     335,000     3,722 a day   fifteen, no reset
chains       166,000     1,844 a day   eleven chapters, one at a time

WHERE THAT LANDS A PLAYER

only the dailies                    tier  59    does not finish
dailies + weeklies                  tier  89    does not finish
dailies + weeklies + monthlies      tier 100    on day 89 of 90
the same, with premium              tier 100    on day 77
everything, free                    tier 100    on day 49
everything, premium                 tier 100    on day 45

THE LINE THAT MATTERS is the third one. A player who does their three dailies and their four weeklies and their two monthlies, and nothing else at all, reaches the hundredth tier on the second to last day of the season. That is the promise: turn up, do the list, finish. The season quests and the chains are not required - they are the room to miss a fortnight and still make it.

Anybody who does clear everything finishes around day fifty and spends the rest of the season in the endless tiers below, which is what they are for.

Neither figure counts the boosters in boosters.yml - weekend x1.5 and happy hour x2 are worth roughly a third again to somebody who plays evenings - nor the catch-up bonus, which only helps whoever is behind. Both of those are slack, on purpose: a season that needs its boosters to be finishable is a season nobody finishes.

Premium arriving sooner rather than earning more per tier is on purpose: whoever pays should finish with a couple of weeks to spare, not halfway through with forty-five days of nothing left to do.

Raising "increment" raises the finish line: every +10 is 49,500 more experience, about seven days of the ordinary rhythm.

season.tiers6 keys
count100
season.tiers.curve5 keys
typeLINEAR
LINEAR      = base + increment * (tier - 1)
EXPONENTIAL = base * multiplier ^ (tier - 1)
FLAT        = always base
base1000
increment100
multiplier1.05
max15000

Ceiling for the cost of a single tier. 0 = none.

season.tiers.overrides2 keys

The tiers that should cost something other than the formula.

508000
10025000
season.tiers
milestone-every10

How many tiers between milestone marks in the GUI. 0 = never.

milestones[1, 25, 75]

Extra milestones, on top of the automatic ones.

season.tiers.icons19 keys

Custom icon for individual tiers. Takes the same full shape as the rewards: material, head texture, model.

Every tier written by hand below gets its own, so that scrolling the ladder shows where the next real prize is instead of a hundred identical panes.

season.tiers.icons.101 key
materialIRON_PICKAXE
18 more entries shaped like 10: 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100

Past the last tier

Whoever finishes the season keeps earning: every "xp" below is worth one prestige, with its own reward. That way the top of the board still has a reason to play in November.

season.infinite3 keys
enabledtrue
xp20000
rewards
["coins: 250", "money: 10000", "broadcast: &d<player> went past the top tier again!"]+1

Which quest pools this season uses

Empty = every pool in quests/. By listing them, a themed season can use only its own.

season
quest-pools
[daily, weekly, monthly, seasonal, chains, premium]+3

Only players who unlocked premium see this pool: the condition lives inside quests/premium.yml, not here.

join-rewards
["coins: 25", "message: <gradient:#FFD54F:#FF8F00>Welcome to Season 1!</gradient>"]+1

Welcome gift the first time the pass is opened.

completion-rewards
["bundle: season-end", "broadcast: <gradient:#FFD54F:#FF8F00><player> completed Season 1!</gradient>"]+1

Reward for reaching the last tier.

season.catch-up2 keys

Bonus for players falling behind. The general values live in config.yml; here you can raise or lower the ceiling for this season only.

enabledtrue
max-multiplier2.0

The rewards

Key = tier, range, "*n" or "default". Inside, one list per track.

Keep reading

Short form, one line:

- "money: 5000"          - "coins: 100"        - "xp: 250"
- "item: DIAMOND 3"      - "tier: 1"           - "reroll: 1"
- "crate: vote 2"        - "booster: 1.5 2h"   - "permission: vip 30d"
- "command: fly <player> 10m"
- "bundle: bundle-name"        (defined in rewards.yml)
- "broadcast: &6..."       - "firework: FFD54F"

Long form, for icons, conditions, randomness and choices: see tier 50 below and docs/06-RICOMPENSE.md.

This table is already complete: every tier gives something to every track, from the first to the hundredth. The "common-crate", "rare-crate" and "epic-crate" are bundles defined in rewards.yml with vanilla content, and so are the kits: they work on a freshly installed server. With a real crate plugin, "bundle: rare-crate" can become "crate: rare 1".

HOW IT IS BUILT

a floor         "default", so no tier is ever an empty pane
five bands      1-10, 11-25, 26-50, 51-75, 76-99: the money grows with
                the number, so the eightieth tier is worth reaching
two patterns    "*5" a crate, "*10" something serious
nineteen tiers  written out by hand, one every five from ten to a
                hundred: those are the ones with a name, an icon of
                their own and something a player remembers - the kits
                at 10 and 15, the cosmetics at 20, 30, 55 and 80, the
                choices at 50 and 85, the storage at 65, and the end

WHAT THE PRICES MEAN

The money here is in the currency of ShadowEconomy, and it is priced against that shop, not invented. In shop.yml a diamond sells for 10 and costs 1,000 to buy back; a netherite ingot costs 12,140, a netherite block 109,260. So:

money: 2,500     two and a half diamonds off the shelf
money: 25,000    two netherite ingots
money: 400,000   three and a half netherite blocks

Over a hundred tiers, bundles counted, the ladder hands out:

free         554,600  and   6,957 pass coins
premium    1,813,000  and  12,195
deluxe     2,795,000  and  17,325

The benchmark to read those against is in quests/weekly.yml: "earn a hundred thousand in a week" is rated HARD there, so honest play is worth roughly fourteen thousand a day. The free ladder adds about six thousand a day on top of that - a real bonus that does not replace playing. The paid ones are deliberately several times that, because that is the thing being sold.

IF YOU THINK THAT IS TOO MUCH for your economy, do not go through this table tier by tier. The premium quests are the bigger half of it: quests/premium.yml hands out two commissions a day at ten to eighteen thousand each, which is about two and a half million over a season. Halving the "money:" lines in that one file is the single change that moves the needle, and it leaves the ladder - the part players actually look at - exactly as it is.

The pass coins are a closed currency: they come from here and from the quests and they go nowhere but shop.yml, where a season's worth buys roughly what a season's worth should. Nothing on those shelves can be bought with a credit card, which is what makes the rest of this safe.

season.rewards27 keys
season.rewards.default3 keys

Every tier that does not appear further down gives at least this.

All three tracks are here, and this is the most important line in the file. A tier where the paid track gives nothing is a grey slot next to a full one: the player who paid reads it as "I have been had", and they are not far wrong. Here every tier gives something to everyone, and the bands and milestones below pile the rest on top.

free["coins: 5", "money: 250"]
premium
["coins: 12", "money: 1000", "item: EXPERIENCE_BOTTLE 4"]+1
deluxe
["coins: 20", "money: 2000", "item: EXPERIENCE_BOTTLE 8"]+1
season.rewards.1-103 keys

The first ten are generous on purpose: that is where it is decided whether somebody carries on or quits.

free["coins: 10", "money: 500", "item: BREAD 8"]
premium
["coins: 25", "money: 2000", "item: COOKED_BEEF 16"]+1
deluxe
["coins: 40", "money: 4000", "item: GOLDEN_APPLE 2", "item: EXPERIENCE_BOTTLE 8"]+2
season.rewards.11-253 keys

Bands: the money grows with the tier, so the eightieth tier is worth reaching and not just another five coins.

free["coins: 6", "money: 400"]
premium
["coins: 15", "money: 1500", "item: EXPERIENCE_BOTTLE 6"]+1
deluxe
["coins: 25", "money: 3000", "item: EXPERIENCE_BOTTLE 12"]+1
13 more entries shaped like default: 26-50, 51-75, 76-99, 15, 25, 35, 40, 55, 60, 70, 75, 80, 90
season.rewards.*53 keys

Every fifth tier, a crate. Careful, this is the easiest trap in the whole file: a key here REPLACES the default and the bands, it does not add to them. Every entry must therefore be written out in full, or tier 5 ends up worth less than tier 4 and the player notices at once.

free
["coins: 10", "money: 600", "bundle: common-crate"]+1
premium
["coins: 25", "money: 2500", "bundle: common-crate", "bundle: snack"]+2
deluxe
["coins: 40", "money: 5000", "bundle: rare-crate", "bundle: snack", "booster: 1.25 30m"]+3
season.rewards.*103 keys

Every tenth, something serious.

free
["coins: 50", "money: 1500", "item: DIAMOND 2", "bundle: common-crate"]+1
premium
["coins: 120", "money: 10000", "bundle: rare-crate", "booster: 1.5 1h"]+2
deluxe
["coins: 200", "money: 20000", "bundle: rare-crate", "bundle: potion-kit", "booster: 1.5 2h"]+3

Exceptions: being exact numbers, they beat the patterns above. From here to the hundredth there is one every five.

season.rewards.103 keys

The first round number. Ten tiers in, a player decides whether this pass is worth the next ninety: this is the answer. A tool, a crate and a line across the screen.

free
["coins: 60", "money: 2500", "bundle: common-crate", "bundle: starter-gear", "title: <gradient:#FFD54F:#FF8F00>TIER 10|<gray>and we are only getting started"]+3
premium
["coins: 140", "money: 12000", "bundle: rare-crate", "bundle: starter-gear", "bundle: snack", "booster: 1.5 2h"]+4
deluxe
["coins: 220", "money: 22000", "bundle: rare-crate", "bundle: starter-gear", "bundle: potion-kit", "booster: 2.0 2h"]+4
season.rewards.203 keys

The first cosmetic. From here on the track alternates: money, then something to use, then something to be seen wearing.

free
["coins: 60", "money: 2500", "item: DIAMOND 3", "bundle: common-crate", "firework: FFD54F"]+2
premium
["coins: 140", "money: 12000", "bundle: rare-crate", "permission: shadowbattlepass.cosmetic.aura 7d", "booster: 1.5 2h"]+3

Seven days of the particle aura the shop sells for eight hundred coins: enough to want it, not enough to stop buying it.

deluxe
["coins: 230", "money: 24000", "bundle: rare-crate", "permission: shadowbattlepass.cosmetic.aura 14d", "item: SHULKER_BOX 1", "booster: 2.0 2h"]+4
season.rewards.303 keys

A named thing, which is the point of a cosmetic: it is not stronger, it is only yours, and everybody can see where it came from.

free
["coins: 110", "money: 4000", "item: DIAMOND 4", "bundle: common-crate"]+1
premium
["coins: 160", "money: 15000", "bundle: rare-crate", { type: ITEM, display: "&cWanderer's Hood", item: {…} }, "booster: 1.5 3h"]+3
deluxe
["coins: 260", "money: 28000", "bundle: rare-crate", "bundle: potion-kit", { type: ITEM, display: "&5Wanderer's Cloak", item: {…} }, "booster: 2.0 3h"]+4
season.rewards.453 keys

The last tier before the halfway crate: gear, so that whoever gets to fifty gets there wearing something.

free
["coins: 140", "money: 9000", "item: DIAMOND_BLOCK 1", "bundle: common-crate"]+2
premium
["coins: 290", "money: 38000", "bundle: rare-crate", "bundle: diamond-kit"]+2
deluxe
["coins: 430", "money: 60000", "bundle: rare-crate", "bundle: diamond-kit", "booster: 2.0 3h"]+3
season.rewards.503 keys
free
["coins: 400", "money: 20000", "item: NETHERITE_SCRAP 2", "bundle: diamond-kit"]+2
premium
["coins: 600", "money: 60000", "bundle: potion-kit", "bundle: epic-crate", { type: CHOICE, display: "&bPick your prize", icon: CHEST_MINECART, options: […] }]+3
type

A choice: the player takes only one, from a menu.

deluxe
["coins: 800", "money: 90000", "bundle: potion-kit", "bundle: epic-crate", "item: SHULKER_BOX 2", "permission: shadowbattlepass.cosmetic.aura 30d", "booster: 2.0 6h"]+5
season.rewards.653 keys

Storage. Unglamorous, and the reward players actually talk about.

free
["coins: 200", "money: 13000", "item: SHULKER_BOX 1"]+1
premium
["coins: 420", "money: 56000", "bundle: rare-crate", "item: SHULKER_BOX 2", "item: TOTEM_OF_UNDYING 1"]+3
deluxe
["coins: 420", "money: 50000", "bundle: epic-crate", "item: SHULKER_BOX 3", "item: NETHERITE_INGOT 1"]+3
season.rewards.853 keys

The second choice of the season. The one at fifty was about what you wear; this one is about how you spend the last fifteen tiers.

free
["coins: 380", "money: 30000", "bundle: rare-crate"]+1
premium
["coins: 550", "money: 80000", { type: CHOICE, display: "&bHow you finish the season", icon: CHEST_MINECART, options: […] }]+1
deluxe
["coins: 780", "money: 90000", "bundle: epic-crate", "item: SHULKER_BOX 3", "booster: 2.0 8h"]+3
season.rewards.953 keys

Five tiers from the end, and the end costs twenty-five thousand on its own. This is the tier that pays for that last climb.

free
["coins: 500", "money: 40000", "bundle: rare-crate", "item: DIAMOND_BLOCK 3"]+2
premium
["coins: 750", "money: 90000", "bundle: epic-crate", "item: NETHERITE_INGOT 3", "booster: 2.0 12h"]+3
deluxe
["coins: 1000", "money: 130000", "bundle: epic-crate", "item: NETHERITE_INGOT 6", "permission: shadowbattlepass.cosmetic.aura 30d", "booster: 2.0 12h"]+4
season.rewards.1003 keys

The last tier. Worth making memorable: it is the one people talk about, and it is the advert for the next season.

free
["coins: 500", "money: 50000", "item: DIAMOND_BLOCK 4", "bundle: season-end"]+2
premium
["coins: 800", "money: 250000", { type: ITEM, display: "<gradient:#FFD54F:#FF8F00>Season 1 Crown</gradient>", item: {…} }, "bundle: season-end", "permission: shadowbattlepass.title.veterano", "firework: FFD54F"]+4

Any head from a head database, by URL or base64: texture: "http://textures.minecraft.net/texture/..."

deluxe
["coins: 1500", "money: 400000", "bundle: epic-crate", "bundle: season-end", "item: NETHERITE_BLOCK 1", { type: ITEM, display: "<gradient:#B388FF:#4527A0>Shadow Crown</gradient>", item: {…} }, "permission: shadowbattlep …+7

Languages

The texts the plugin shows to players, one file per language.

lang/

Texts and messages
2 files
plugins/ShadowBattlePass/lang/
File notes

MiniMessage format. Placeholders are written <like_this>. This is the final fallback: a key missing from every other language file is looked up here, one key at a time. A key missing from this file on disk is read from the copy inside the plugin jar.

Every message the plugin shows to players, one file per language. The keys are not listed one by one: here are the files, how many texts they hold and the placeholders they use.

FileLanguageTexts
lang/en_US.yml reference English 727
lang/it_IT.yml Italiano 727

Most used placeholders

The plugin replaces them with the right value: leave them written as they are.

  • <prefix>
  • <player>
  • <id>
  • <tier>
  • <count>
  • <amount>
  • <track>
  • <value>
  • <season>
  • <target>
  • <xp>
  • <coins>
  • <quest>
  • <goal>

Message groups

  • prefix 1
  • common 19
  • command 10
  • input 4
  • condition 26
  • reward 25
  • mailbox 3
  • pass 47
  • quest 51
  • objective 68
  • track 17
  • shop 28
  • wallet 1
  • leaderboard 23
  • info 18
  • challenge 17
  • choice 2
  • notify 24
  • booster 4
  • season 3
  • streak 4
  • team 1
  • recap 26
  • stat 12
  • voucher 12
  • gift 18
  • hologram 4
  • admin 216
  • license 38
  • locked 5

Other

Ready-made content, examples, imports: the files you copy and edit as a model.

quests/

Quests, one group per file
6 files
plugins/ShadowBattlePass/quests/

6 files with the same structure. Below, 4 of them in full, one for each different shape; the others are listed by name.

  • chains.yml
  • daily.yml
  • monthly.yml
  • premium.yml
  • seasonal.yml
  • weekly.yml

Example quests/daily.yml

File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/quests/daily.yml

Copy it over this file to use it (copiala al posto di questo file).

ONE FILE = ONE QUEST POOL. A new file in this folder is a new pool: there is nothing to register anywhere.

The pool decides the rhythm (daily, weekly, ...) and how many quests a player gets at a time. Writing thirty daily quests and handing out three a day at random is what keeps a pass alive for ninety days without writing ninety days of content.

The draw is stable: the same player, on the same day, always gets the same quests, even after twenty restarts.

Every objective type:           docs/05-OBIETTIVI.md
Every condition:                docs/07-CONDIZIONI.md
Every reward:                   docs/06-RICOMPENSE.md
config-version2
pool12 keys
iddaily
display"<yellow><bold>Daily</bold>"
description
["&7Three new quests every day.", "&7The rotation is wide: in ninety days", "&7the same three hardly ever come back."]+2
periodDAILY
iconCLOCK
order0
size3

How many a player gets per cycle. "all" hands out every one.

sequentialfalse
false = the plugin picks the quests at random, by weight.
true  = they are handed out in file order, one after another (storyline).
auto-assigntrue
pool.rerolls3 keys
amount1

How many times a day a quest you dislike can be swapped.

cost-coins0

Cost in pass coins, on top of the free one. 0 = free.

cost-money0
pool
completion-xp800

Bonus for finishing all three in one day. It is deliberately large: three quests plus this bonus is the daily rhythm the tier curve in seasons/season-1.yml is tuned against.

completion-rewards
["coins: 50", "message: <gradient:#FFD54F:#FF8F00>Day complete!</gradient>"]+1
The quests

The smallest form is six lines:

quest-id:
  display: "&bTitle"
  xp: 500
  objectives:
    - type: MOB_KILL
      amount: 30

Everything else has a sensible factory value.

weight: how likely it is to come up. 2 = twice as likely as a 1. mode: all (every objective) or any (one is enough).

quests39 keys
trades
quests.miner8 keys
display"&bTireless miner"
description["&7Go down to where the stone turns dark."]
quests.miner.icon1 key
materialDIAMOND_PICKAXE
quests.miner
difficultyEASY
weight2.0
xp600
objectives[{ type: BLOCK_BREAK, target: […], amount: 300…]
no-placed-blocks

A block you placed yourself does not count.

rewards["coins: 20"]
35 more entries shaped like miner: prospector, lumberjack, farmer, fisher, nether, duelist, wanderer, sightseer, attendance, breeder, trader, enchanter, brewer, crafter, smelter, well-fed, sleeper, blacksmith, grinder, glassblower, bricklayer, coppersmith, spelunker, scavenger, sniper, arachnophobe, tank, sprinter, swimmer, boatman, horseman, jumper, shearer, achiever, pyrotechnic
combat
quests.hunter8 keys
display"&cNight hunter"
description["&7Monsters do not kill themselves."]
quests.hunter.icon1 key
materialIRON_SWORD
quests.hunter
difficultyMEDIUM
weight2.0
xp800
objectives[{ type: MOB_KILL, target: […], amount: 50, no…]
no-spawner-mobs

Spawner mobs do not count: that would be a lever, not a hunt.

rewards["coins: 30"]
exploration
assorted
quests.full-day9 keys

A quest with two objectives, both of which must be done.

display"&6A full day"
description["&7A bit of everything."]
quests.full-day.icon1 key
materialBUNDLE
quests.full-day
difficultyHARD
weight0.6
xp1500
modeall
objectives
[{ type: BLOCK_BREAK, amount: 200, no-placed-blocks: true }, { type: MOB_KILL, amount: 25, no-spawner-mobs: true }, { type: ITEM_CRAFT, amount: 32 }]+2
rewards["coins: 60", "bundle: snack"]
quests.your-way9 keys

A quest where any one of the objectives is enough.

display"&dYour way"
description
["&7Do any one of these things.", "&8Finishing one is enough."]+1
quests.your-way.icon1 key
materialCOMPASS
quests.your-way
difficultyMEDIUM
weight0.8
xp900
modeany
objectives
[{ type: ORE_MINE, amount: 100 }, { type: MOB_KILL, amount: 60 }, { type: ITEM_FISH, amount: 30 }, { type: CROP_HARVEST, amount: 250 }]+3
rewards["coins: 35"]
everyday jobs
the workshop

The anvil, the grindstone, the furnace: blocks most servers have in spawn and nobody ever has a reason to walk up to twice.

underground
combat, the other half
on the road

Example quests/chains.yml

File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/quests/chains.yml

Copy it over this file to use it (copiala al posto di questo file).

A STORY, NOT A LIST.

A pool with "sequential: true" draws nothing at random: it hands out the quests in file order, one after another, and the next one appears only once the previous one is finished.

Two keys are enough to build one:

requires: [ id ]   this quest appears only after that one
hidden: true       until unlocked it is not even visible

Together they make a chapter that opens as you reach it, instead of a list of sixteen grey entries nobody reads.

"chain" and "step" only serve to group and order them in the GUI: two different chains in the same file live together happily - and this file holds two of them.

THE SEASON READS AS ONE BOOK

La Via del Cercatore   six chapters, from a wooden pickaxe to the
                       thing at the bottom of the world.
Il Lascito             five more, opened by the last chapter of the
                       first: what a seeker does once they have found
                       it. Building, trading, the End, and a name that
                       stays on the server after the season.

Eleven chapters over ninety days, one held at a time. The second chain is hidden until the first is over, so a new player sees a beginning and not a wall of eleven grey entries.

config-version2
pool12 keys
idchains
display"<gradient:#B388FF:#4527A0><bold>The Seeker's Path</bold></gradient>"
description
["&7Two stories, eleven chapters.", "&7The next one opens when you finish this one."]+1
periodSEASON
iconWRITTEN_BOOK
order4
size1

How many a player holds at a time. With a chain, one.

sequentialtrue

This is the line that turns it into a story.

auto-assigntrue
pool.rerolls1 key
amount0
pool
completion-xp45000
completion-rewards
["coins: 1500", "money: 150000", "bundle: epic-crate", "permission: shadowbattlepass.title.cercatore", "broadcast: <gradient:#B388FF:#4527A0><player> read both stories to the very end.</gradient>"]+3
quests11 keys
chapter 1
quests.path-110 keys
display"&fI. The first strike"
description
["&7Every seeker starts out with a pickaxe", "&7and no idea what they are looking for."]+1
quests.path-1.icon1 key
materialWOODEN_PICKAXE
quests.path-1
chainvia-del-cercatore
step1
difficultyEASY
xp1000
objectives[{ type: BLOCK_BREAK, target: […], amount: 100…]
rewards["item: STONE_PICKAXE 1", "coins: 25"]
unlocks[path-2]
8 more entries shaped like path-1: path-2, path-4, path-5, path-6, legacy-1, legacy-2, legacy-3, legacy-5
chapter 3
quests.path-313 keys
display"&bIII. The deep dark"
description
["&7Below Y zero there is no light left.", "&7Only black stone, and whatever lives in it."]+1
quests.path-3.icon1 key
materialDEEPSLATE
quests.path-3
chainvia-del-cercatore
step3
difficultyMEDIUM
hiddentrue
requires[path-2]
xp4000
modeall
objectives
[{ type: BLOCK_BREAK, target: […], amount: 500, max-y: 0, no-placed-blocks: true }, { type: MOB_KILL, amount: 50, max-y: 0, no-spawner-mobs: true }]+1
max-y

Only below level zero: that is where the real descent starts.

rewards["item: DIAMOND 4", "coins: 100"]
unlocks[path-4]
Il Lascito - five chapters

Opens when the Way of the Seeker is over. The first story is about going down; this one is about what gets built after coming back up. It is gated on the last chapter of the first, so the two never show at the same time.

chapter 4
quests.legacy-413 keys
display"&dIV. The sky at the end"
description
["&7The last door was always open.", "&7Nobody ever looked up, that is all."]+1
quests.legacy-4.icon1 key
materialEND_CRYSTAL
quests.legacy-4
chainil-lascito
step4
difficultyEPIC
hiddentrue
requires[legacy-3]
xp16000
modeall
objectives
[{ type: BOSS_KILL, target: […], amount: 1 }, { type: MOB_KILL, target: […], amount: 24, no-spawner-mobs: true }]+1
type

The dragon and the shulkers say "you have been to the End" without naming a world: a server that renamed its worlds still works.

rewards
["item: SHULKER_BOX 2", "item: DRAGON_BREATH 8", "coins: 450", "money: 60000"]+2
unlocks[legacy-5]

Example quests/premium.yml

File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/quests/premium.yml

Copy it over this file to use it (copiala al posto di questo file).

THE QUESTS ONLY PREMIUM OWNERS SEE.

A quest pool can carry conditions, and until they are true the pool does not exist: no grey tab, no locked quests to stare at longingly. Here there is a single condition:

conditions:
  - "premium: true"

The moment somebody unlocks the premium track - by buying it, by receiving the permission, or with /bpa track give - its two quests show up within half a minute, with no restarts and no reloads.

Why the experience is low

The quests in here give 300-450 experience, less than the ordinary dailies which give 500-900. That is not an oversight.

Keep reading

The premium advantage lies in the REWARDS, not in the speed. If premium quests also gave far more experience, whoever pays would finish the season halfway through and spend the second half with nothing to do - which is the quickest way to make the pass feel like a swindle to the very people who bought it. This way they reach the end a few days ahead of everyone else, and have earned a great deal more along the way.

If you want the opposite, a premium that races ahead, raise "xp" in here: they are numbers, nothing is sacred. Just remember to redo the maths in seasons/season-1.yml, because the tier curve is tuned to how much experience comes in per day.

How to make another one

Copy this file, change "id", and add the new id to the season's quest-pools entry. The conditions can be anything: "track: deluxe" for a pool reserved to the highest track, "permission: ..." for a rank, "playtime: 10h" for players who have been around a while. They are the same ones used everywhere else in the plugin, and they are explained in docs/07-CONDIZIONI.md.

config-version2
pool12 keys
idpremium
display"<gold><bold>Premium</bold>"
description
["&7Two commissions a day, just for you.", "&7A little more experience, many more rewards."]+1
periodDAILY
iconGOLD_INGOT
order3
size2
auto-assigntrue
conditions["premium: true"]

THE line that makes this pool exclusive. Remove it and it becomes an ordinary pool, visible to everyone.

pool.rerolls2 keys

Two free rerolls instead of one: an advantage felt every day that costs the season's balance nothing.

amount2
cost-coins0
pool
completion-xp300
completion-rewards["coins: 50", "money: 5000"]
quests24 keys
quests.premium-digging8 keys
display"&6Commission: stone"
description
["&7Five hundred blocks.", "&8Pays twice the usual."]+1
quests.premium-digging.icon1 key
materialGOLDEN_PICKAXE
quests.premium-digging
difficultyMEDIUM
weight1.8
xp400
objectives[{ type: BLOCK_BREAK, amount: 500, no-placed-b…]
rewards["coins: 60", "money: 12000"]
23 more entries shaped like premium-digging: premium-ores, premium-hunt, premium-foundry, premium-harvest, premium-luck, premium-jeweller, premium-night, premium-survey, premium-ledger, premium-timber, premium-quarry, premium-fishmonger, premium-market, premium-stable, premium-granary, premium-armoury, premium-apothecary, premium-caravan, premium-nether, premium-masonry, premium-forge, premium-contract, premium-vault
the trades
the long shifts

Two quests a day for ninety days is a hundred and eighty draws. With six of them a premium owner sees the same commission every third day and stops reading it; with twenty-four they do not.

Example quests/seasonal.yml

File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/quests/seasonal.yml

Copy it over this file to use it (copiala al posto di questo file).

Quests that last as long as the season and never reset. They are all handed out together ("size: all"), so from day one a player can see what the next three months hold.

THEY ARE THE SHAPE OF THE SEASON

Thirteen of them, and they are deliberately not thirteen versions of the same wall of numbers. Four are totals that only a player who logs in most days will reach, four reward going somewhere rather than repeating something, three are one-off feats that can be done in an afternoon by whoever knows how, and two are about the pass itself. Between them they say what this season is: exploration, digging, and coming back.

None of them can be finished on day one, and none of them needs the last week: the season has a shape rather than a deadline.

config-version2
pool11 keys
idseasonal
display"<gradient:#FFD54F:#FF8F00><bold>Seasonal</bold></gradient>"
description
["&7They last as long as the season.", "&7No reset, no hurry."]+1
periodSEASON
iconNETHER_STAR
order3
sizeall
auto-assigntrue
pool.rerolls1 key
amount0
pool
completion-xp50000
completion-rewards
["coins: 2000", "money: 250000", "bundle: season-end", "permission: shadowbattlepass.title.leggenda", "broadcast: <gradient:#FFD54F:#FF8F00><player> finished every seasonal quest!</gradient>"]+3

The same title the hundredth tier of the deluxe track gives. Two roads to one badge is deliberate: the player who cleared every season quest earned it as much as the one who climbed to the top.

quests15 keys
quests.hundred-thousand7 keys
display"&bA hundred thousand"
description["&7A hundred thousand blocks over the whole s…]
quests.hundred-thousand.icon1 key
materialNETHERITE_PICKAXE
quests.hundred-thousand
difficultyEPIC
xp25000
objectives[{ type: BLOCK_BREAK, amount: 100000, no-place…]
rewards["coins: 1500", "bundle: epic-crate"]
14 more entries shaped like hundred-thousand: legend, globetrotter, three-worlds, loyal, companion, deep-hundred, builder-of-things, harvest-season, long-road, dragonslayer, deep-dark, ancient-hoard, the-climb, hundred-quests
the totals
the road
the feats

templates/

Season templates
13 files
plugins/ShadowBattlePass/templates/

13 files with the same structure. Below, 4 of them in full, one for each different shape; the others are listed by name.

  • index.yml
  • pvp/daily.yml
  • pvp/premium.yml
  • pvp/season.yml
  • pvp/weekly.yml
  • skyblock/daily.yml
  • skyblock/premium.yml
  • skyblock/season.yml
  • skyblock/weekly.yml
  • survival/daily.yml
  • survival/premium.yml
  • survival/season.yml
  • survival/weekly.yml

Example templates/survival/season.yml

Template "survival" - the season

File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/templates/survival/season.yml

Once the template is applied, copy it over seasons/survival.yml (copiala al posto di quel file dopo /bpa template apply survival).

Fifty tiers in thirty days, two tracks: free and premium.

THE MATHS, so you know what you are installing and what to change:

The season is worth 99,000 experience in total (linear curve: 1,000 at the first tier, 2,960 at the fiftieth).

A FREE player doing their quests every day earns:

3 dailies x ~600          = 1,800
full-day bonus            =   500
weeklies (2 x 4,000)      = ~1,150 a day
about 3,450 a day -> finishes around day 28

A PREMIUM player also has two quests a day worth 400:

about 4,250 a day  ->  finishes around day 23, and from there
on carries into the endless tiers.

If your server is more or less active than that, the right knob is "increment" below: raising it lengthens the season, lowering it shortens it. Do not touch "count" once a season has started.

config-version2
season15 keys
idsurvival
display"<gradient:#66BB6A:#1B5E20><bold>Survival Season</bold></gradient>"
description
["&7Thirty days. Fifty tiers.", "&7Dig, build, survive."]+1
season.icon2 keys
materialGRASS_BLOCK
glowtrue
season
enabledtrue
duration30d
auto-start-nextfalse
The two tracks
season.tracks2 keys
season.tracks.free5 keys
display"<green><bold>Free</bold>"
description["&7For everyone, always."]
iconLIME_DYE
defaulttrue
order0
season.tracks.premium6 keys
display"<gold><bold>Premium</bold>"
description
["&7One more reward at every tier,", "&7and two quests a day just for you.", "", "<gray>Unlock it and you get <white>right away</white> everything", "&7you had already earned."]+4
iconGOLD_INGOT
order1
permission"shadowbattlepass.premium"

The node to hand out from your store.

unlock-retroactivetrue
The tiers
season.tiers5 keys
count50
season.tiers.curve3 keys
typeLINEAR
base1000
increment40
season.tiers.overrides2 keys

The two tiers that should weigh more than the rest.

253000
506000
season.tiers
milestone-every10
milestones[1, 25]
season.infinite3 keys
enabledtrue
xp5000
rewards["coins: 100", "money: 5000"]
season
quest-pools
[survival-daily, survival-weekly, survival-premium]+1

Only this template's quests: the plugin's sample dailies stay out, so that two different balancings do not get mixed.

join-rewards
["coins: 25", "item: BREAD 16", "message: <gradient:#66BB6A:#1B5E20>Welcome to the season!</gradient> <gray>Open <white>/bp</white> to begin."]+1
completion-rewards
["coins: 1000", "money: 100000", "booster: 2.0 24h", "broadcast: <gradient:#66BB6A:#1B5E20><player> completed the Survival Season!</gradient>"]+2
season.catch-up2 keys
enabledtrue
max-multiplier2.0
The rewards

This is the rhythm, and it is worth understanding before changing it:

Keep reading
every tier          free: coins              premium: coins + money
every 5 tiers       free: useful materials   premium: a crate
every 10 tiers      free: something good
                    premium: rare crate + booster
tier 25             the mid-season gift
tier 50             the one people talk about

The "bundle" entries are the packs in rewards.yml: change those and you change every tier that uses them in one go.

season.rewards8 keys
season.rewards.default2 keys
free["coins: 8"]
premium["coins: 15", "money: 1000"]
season.rewards.1-52 keys

The first five tiers are generous on purpose: that is where a player decides whether this pass is for them or not.

free["coins: 20", "money: 1500"]
premium
["coins: 40", "money: 4000", "item: IRON_INGOT 8"]+1
6 more entries shaped like default: *5, *10, 10, 25, 40, 50

Example templates/skyblock/daily.yml

Template "skyblock" - the daily quests

File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/templates/skyblock/daily.yml

Once the template is applied, copy it over quests/skyblock-daily.yml (copiala al posto di quel file dopo /bpa template apply skyblock).

Twelve quests, three a day. None asks you to go anywhere: on an island you do not travel, you produce.

NOTE: the quests that depend on another plugin (earning money, raising the island level) are deliberately absent, so that this template works on a freshly installed server too. Adding them is easy, and the example is at the bottom of the file.

config-version2
pool11 keys
idskyblock-daily
display"<yellow><bold>Daily</bold>"
description["&7Three new quests every day."]
periodDAILY
iconCLOCK
order0
size3
auto-assigntrue
pool.rerolls2 keys
amount1
cost-coins0
pool
completion-xp500
completion-rewards
["coins: 30", "message: <gradient:#FFD54F:#FF8F00>Day complete!</gradient>"]+1
quests12 keys
the generator
quests.skyblock-generator8 keys
display"&7The generator"
description["&7Five hundred blocks from the cobblestone g…]
quests.skyblock-generator.icon1 key
materialCOBBLESTONE
quests.skyblock-generator
difficultyEASY
weight2.2
xp500
objectives[{ type: BLOCK_BREAK, target: […], amount: 500…]
no-placed-blocks

On a generator blocks are not "placed by you": they are born. If your generator uses blocks the plugin sees as placed, remove this line.

rewards["coins: 15"]
11 more entries shaped like skyblock-generator: skyblock-furnace, skyblock-harvest, skyblock-cane, skyblock-breeding, skyblock-trees, skyblock-crafter, skyblock-enchanter, skyblock-trader, skyblock-mobs, skyblock-fishing, skyblock-your-way
The quests that depend on another plugin

Uncomment them once that plugin is present. Without it they would sit at zero and waste one of the day's three slots.

skyblock-earnings:
   display: "&6Chi lavora guadagna"
   icon:
     material: GOLD_INGOT
   difficulty: MEDIUM
   weight: 1.2
   xp: 650
   objectives:
- type: MONEY_EARN        # requires ShadowEconomy
       amount: 50000
   rewards:
     - "coins: 20"
skyblock-island:
   display: "&bL'isola cresce"
   icon:
     material: BEACON
   difficulty: HARD
   weight: 0.8
   xp: 900
   objectives:
- type: ISLAND_LEVEL      # requires ShadowSkyBlock
       amount: 50
   rewards:
     - "coins: 40"

Example templates/pvp/daily.yml

Template "pvp" - the daily quests

File notes

ITALIAN VERSION (versione italiana), in this plugin's folder:

esempi/it_IT/templates/pvp/daily.yml

Once the template is applied, copy it over quests/pvp-daily.yml (copiala al posto di quel file dopo /bpa template apply pvp).

Eleven quests, three a day, between 500 and 900 experience.

All of them can be done alone against mobs: nobody should depend on how many people are online to make progress. The quest that asks you to kill PLAYERS is the only exception, and it is commented out at the bottom with its reasons.

config-version2
pool11 keys
idpvp-daily
display"<yellow><bold>Daily</bold>"
description["&7Three new quests every day."]
periodDAILY
iconCLOCK
order0
size3
auto-assigntrue
pool.rerolls2 keys
amount1
cost-coins0
pool
completion-xp400
completion-rewards["coins: 30", "item: GOLDEN_APPLE 2"]
quests11 keys
fighting
quests.pvp-hunt8 keys
display"&cHunting party"
description
["&7Sixty monsters.", "&8Mobs from spawners do not count."]+1
quests.pvp-hunt.icon1 key
materialIRON_SWORD
quests.pvp-hunt
difficultyEASY
weight2.2
xp550
objectives[{ type: MOB_KILL, amount: 60, no-spawner-mobs…]
rewards["coins: 18"]
10 more entries shaped like pvp-hunt: pvp-damage, pvp-skeletons, pvp-hostiles, pvp-nether, pvp-endure, pvp-smith, pvp-anvil, pvp-supplies, pvp-ores, pvp-your-way
The player-killing quest

Uncomment it only if you know what you are doing.

On a quiet server it is impossible and burns a slot for the whole day; on a busy one it becomes an incentive to camp the spawn exit. If you do switch it on, keep the number low (three is plenty) and the weight low, so it rarely comes up.

pvp-duel:
  display: "&4Duellante"
  description:
    - "&7Tre giocatori."
  icon:
    material: PLAYER_HEAD
  difficulty: HARD
  weight: 0.5
  xp: 900
  objectives:
    - type: PLAYER_KILL
      amount: 3
  rewards:
    - "coins: 50"

Example templates/index.yml

File notes

The catalogue of ready made battle passes.

This file lives INSIDE the jar and is never extracted: the templates you do not apply never clutter the plugin folder.

/bpa template list
/bpa template info <id>
/bpa template apply <id>

Applying copies the files into place and nothing more: it starts nothing and overwrites nothing. From then on they are ordinary YAML, editable by hand or with the in-game editor.

Under "files" the key is the file name inside the template folder WITHOUT .yml (a dot in a YAML key would be read as a nested path), and the value is where it lands in the plugin folder. So "season: seasons/survival.yml" means: take templates/survival/season.yml and write it to seasons/survival.yml.

config-version2
templates3 keys
Classic survival
templates.survival7 keys
display"<gradient:#66BB6A:#1B5E20><bold>Survival</bold></gradient>"
flavour"Mine, build, survive"
description
["The pass for an ordinary survival server.", "Quests for digging, gathering, fighting and exploring,", "tuned for someone who plays an hour or two a day.", "", "Whoever plays every day reaches the end around day 28."]+4
tiers50
days30
quests28
templates.survival.files4 keys
season"seasons/survival.yml"
daily"quests/survival-daily.yml"
weekly"quests/survival-weekly.yml"
premium"quests/survival-premium.yml"
SkyBlock
templates.skyblock7 keys
display"<gradient:#4FC3F7:#0288D1><bold>SkyBlock</bold></gradient>"
flavour"Island, farms, economy"
description
["The pass for a skyblock server.", "Quests about generators, farms, trading and money,", "with little that depends on exploring.", "", "It works well with the island team pass."]+4
tiers50
days30
quests25
templates.skyblock.files4 keys
season"seasons/skyblock.yml"
daily"quests/skyblock-daily.yml"
weekly"quests/skyblock-weekly.yml"
premium"quests/skyblock-premium.yml"
PvP and combat
templates.pvp7 keys
display"<gradient:#EF5350:#B71C1C><bold>PvP</bold></gradient>"
flavour"Fight, hunt, outlive the others"
description
["The pass for a server where people fight.", "Quests about kills, damage, gear and bosses,", "with rewards meant for those who keep coming back to the arena.", "", "Shorter and steeper: 40 tiers in 30 days."]+4
tiers40
days30
quests23
templates.pvp.files4 keys
season"seasons/pvp.yml"
daily"quests/pvp-daily.yml"
weekly"quests/pvp-weekly.yml"
premium"quests/pvp-premium.yml"

All the documentation