Skip to content

Information

BattleStudio Plugin

OptimalMilestones

Long-run progression tracks: numbered milestones per category, each with its own threshold and rewards to claim.

  • Paper / Folia
  • MC 1.20+
  • Java 21+
  • PlaceholderAPI

OptimalMilestones gives players one or more categories - Blocks, Playtime, Prestige, whatever you define - and each category is a numbered track of milestones. A milestone has a cumulative Required threshold and a set of rewards. Reach the threshold and the milestone becomes claimable; claim it once and the rewards run.

Every category is one file. Add a file, get a new track, complete with its own icon in the menu’s category row.

A category tracks progress in one of two ways, set by Progress.Type:

Type Progress comes from Admin-adjustable
API An internal counter fed by a single named event source. Every matching event adds +1. Yes
PLACEHOLDER A numeric PlaceholderAPI placeholder, read live every time it’s needed. No

API categories store their counter in the plugin’s own database, so /milestonesadmin progress can adjust them. PLACEHOLDER categories own nothing - they just read whatever the placeholder says, so the plugin refuses to modify them.

One source per category, no filters - no chance roll, no world or block whitelist. Every matching event is +1.

Source Fires on
BLOCK_BREAK Bukkit BlockBreakEvent
BLOCK_PLACE Bukkit BlockPlaceEvent
MOB_KILL A non-player entity dies with a player killer
PINNAPRISON PinnaPrison’s BlockMineEvent
EDDUNGEONS EdDungeons’ EdDungeonsSwingSwordEvent
RIVALHARVESTERHOE RivalHarvesterHoes’ RivalBlockBreakEvent

Names are parsed case-insensitively and - counts as _, so block-break works. An unknown name fails the load loudly. The last three are hooked purely by reflection and register only when their plugin is installed.

Required is a cumulative total, not a per-step amount. Milestone 3 at Required: 5000 means 5,000 total, not 5,000 more than milestone 2.

Milestones are never claimed automatically. When progress crosses a threshold the milestone turns claimable and a MILESTONE-UNLOCKED message fires (a timer scans every Notify-Interval seconds). The player then clicks it in /milestones. The claim is written to the database immediately, so a crash can’t re-grant it, and the milestone’s Commands run from console.

Claims never expire and progress is never reset by the plugin.

  • Any number of categories, one file each, each with its own icon and menu slot
  • Two progress types: an internal event counter or a live PlaceholderAPI value
  • Per-category milestone icons, so each track formats its own units and colors
  • Cumulative thresholds, Roman-numeral labels, configurable progress bar
  • Paginated milestone grid, opening on the player’s first unclaimed milestone
  • Unlock notifications with an optional screen title
  • Temporary feedback icons on blocked clicks
  • Full PlaceholderAPI output, per category
  • SQLite storage with write-behind flushing
  • Folia-ready (region schedulers throughout)

Player - /milestones (aliases /ms, /milestone):

  • /milestones - open the menu on the default category · optimalmilestones.use
  • /milestones <category> - open a specific category · optimalmilestones.use

Admin - /milestonesadmin (aliases /msadmin, /milestoneadmin) · all optimalmilestones.admin:

  • /milestonesadmin progress <give|take|set> <player> <category> <amount> - adjust a tracked counter. API categories only - a PLACEHOLDER category answers CATEGORY-NOT-TRACKED. Works offline.
  • /milestonesadmin reset <player> [category] - wipe progress and claims, for one category or all of them. Works offline.
  • /milestonesadmin reload - reload every config file.
  • optimalmilestones.use - /milestones and the menu (default: everyone)
  • optimalmilestones.admin - every /milestonesadmin command (default: op)

Milestones and categories have no permission nodes of their own.

Requires PlaceholderAPI. Identifier optimalmilestones. Every placeholder is per category - swap <category> for the category’s id (its file name without .yml).

  • %optimalmilestones_<category>_progress% - raw progress
  • %optimalmilestones_<category>_progress_formatted% - same, comma-grouped (1,234)
  • %optimalmilestones_<category>_completed% - milestones claimed
  • %optimalmilestones_<category>_total% - milestones in the category
  • %optimalmilestones_<category>_reached% - milestones whose threshold is met (claimed or not)
  • %optimalmilestones_<category>_percent% - claimed ÷ total as a rounded percentage

For example, %optimalmilestones_blocks_progress%. An unknown category returns empty.

PLACEHOLDER categories return 0 for _progress when the player is offline, since the value can only be read live.

Every text field supports legacy & codes, &#RRGGBB hex, MiniMessage (<gradient:a,b,c>, <rainbow>, <bold>, <#RRGGBB>, <solid:HEX>, …) and PlaceholderAPI %...% in messages and action arguments. <gradient> accepts comma-separated hex stops.