Skip to content

Installation

Install OptimalMilestones and OptimalLibrary on your Paper or Folia server.

  • OptimalLibrary - required. It is a separate free plugin that provides the shared runtime (text and MiniMessage, menus, storage, PlaceholderAPI support). OptimalMilestones will not enable without it. Keep both plugins up to date together.
  • Paper or Folia (or a Paper fork). Spigot is not supported. Supported server versions: 1.21.11, 26.1.1, 26.1.2, 26.2 and 26.3.
  • Java 21 for 1.21.11, Java 25 for the 26.x servers. Check with java -version.

Optional soft dependencies (used only when installed):

  • PlaceholderAPI - needed for the %optimalmilestones_...% placeholders, and required for any category using Progress.Type: PLACEHOLDER. Without it those categories always read 0 and never unlock.
  • PinnaPrison, EdDungeons, RivalHarvesterHoes - extra API progress sources (PINNAPRISON, EDDUNGEONS, RIVALHARVESTERHOE). Each hook registers only when its plugin is installed, and logs Hooked <plugin>. on startup.

Milestone rewards are console commands, so whatever plugin hands out the reward (crates, boosters, economy) needs to be installed too. OptimalMilestones does not hook it directly.

  1. Check java -version matches your server version (21 for 1.21.11, 25 for 26.x).
  2. Stop the server.
  3. Drop OptimalLibrary.jar and OptimalMilestones.jar into plugins/.
  4. (Recommended) Add PlaceholderAPI.jar, plus any of the plugins above you want as progress sources.
  5. Start the server. The plugin generates its files and prints a startup banner with the number of categories and milestones loaded.
  6. Run /plugins - OptimalLibrary and OptimalMilestones should both be green.

On the first start OptimalLibrary downloads its libraries (the SQLite and MySQL drivers, HikariCP and XSeries) through Paper’s library loader, so the server needs internet access and the first launch takes a few extra seconds.

plugins/OptimalMilestones/
├─ config.yml # save/notify intervals, progress bar, sounds
├─ messages.yml # every message
├─ guis/
│ └─ milestones-menu.yml # the /milestones menu layout and temporary icons
├─ milestones/ # one file per category, id = file name
│ ├─ blocks.yml # API category, BLOCK_BREAK source, 24 milestones
│ └─ playtime.yml # PLACEHOLDER category, %statistic_hours_played%, 12 milestones
└─ data/
└─ data.db # SQLite player data (do not edit by hand)

The milestones/ folder is only filled with the two examples when it does not exist yet. Delete or rename the examples freely, they will not come back unless you delete the whole folder.

The example rewards use crates, booster, cubes, dragoneggs, dragonbox, tokens, credits and lootbox commands. Point them at your own plugins before going live. The playtime category reads %statistic_hours_played%, which needs PlaceholderAPI’s Statistic expansion (/papi ecloud download Statistic).

Progress and claims are stored in SQLite at data/data.db, in a player_milestones table with one row per player and category (the counter plus the list of claimed milestone numbers). There is no MySQL option for this plugin.

  • A player’s data is loaded while they log in. If it cannot be loaded, the login is refused with the PROFILE-LOAD-FAILED message, so nobody plays with an empty profile that would overwrite their real one.
  • Changes are kept in memory and saved every Settings.Save-Interval seconds (default 60), on quit and on server shutdown.
  • A claim is saved immediately, so a crash can never let a player claim the same milestone twice.
  • PLACEHOLDER categories store only their claims. The progress number is read live from PlaceholderAPI.

Continue to Creating Categories, or read the Information overview first.