An easily configurable advanced mining level system for Spigot/Minecraft servers. Players gain XP by mining configured blocks, level up through a progression system, unlock skills (haste, instant break, extra ore drops), and claim item rewards.
Version: 1.3.0 Author: ChafficPlugins Server API: Paper 1.21 Java: 21
Maven project. Build with:
mvn clean verify # compile + test + package
mvn test # run tests only
mvn package -DskipTests # package without tests
The Maven Shade Plugin bundles dependencies into the final JAR. CI uses GitHub Actions (.github/workflows/ci.yml) with JDK 21 on ubuntu-latest, triggered on push/PR to master.
| Dependency | Version | Scope | Purpose |
|---|---|---|---|
| Paper API | 1.21.11-R0.1-SNAPSHOT | provided | Server framework |
| CrucialLib | 3.0.0 | compile | GUI framework, localization, JSON I/O, utilities |
| PlaceholderAPI | 2.11.5 | provided | Placeholder expansion (optional) |
| JUnit Jupiter | 5.11.0 | test | Unit testing |
| MockBukkit | 4.101.0 | test | Bukkit mock for unit tests |
Both CrucialLib and PlaceholderAPI are soft dependencies. CrucialLib is auto-downloaded from GitHub releases if missing. PlaceholderAPI features gracefully degrade when absent.
src/main/java/de/chafficplugins/mininglevels/
├── MiningLevels.java # Main plugin class (onLoad/onEnable/onDisable, config)
├── api/
│ ├── MiningPlayer.java # Player progression data model (UUID, level, XP, rewards)
│ ├── MiningLevel.java # Level definition (skills, rewards, commands, level-up logic)
│ ├── MiningBlock.java # Block config (materials, XP value, min level required)
│ └── Reward.java # ItemStack wrapper for JSON serialization
├── listeners/
│ ├── MiningLevelsCommandListener.java # /mininglevels command dispatcher
│ ├── RewardCommandListener.java # /miningrewards command handler
│ ├── commands/
│ │ └── LevelingCommands.java # Static implementations for setlevel, setxp, level, leaderboard
│ └── events/
│ ├── MiningEvents.java # Core mining: block damage/break, XP awards, haste, instabreak, extra ore
│ ├── ServerEvents.java # Player join (create profile) / quit (save data)
│ └── NoXpBlockEvents.java # Tracks player-placed/generated blocks, TNT explosion filtering
├── gui/
│ ├── leaderboard/
│ │ ├── LeaderboardList.java # Top players GUI
│ │ └── MiningLevelProfile.java # Individual player stats GUI
│ ├── levels/
│ │ ├── LevelList.java # Level management list GUI
│ │ └── LevelEdit.java # Level editor GUI (skills, rewards, XP)
│ └── blocks/
│ ├── BlockList.java # Block management list GUI
│ └── BlockEdit.java # Block editor GUI (materials, XP, min level)
├── io/
│ ├── FileManager.java # File creation, downloads, JSON persistence
│ └── MessagesYaml.java # messages.yml template creation
├── placeholders/
│ ├── LevelPlaceholders.java # PlaceholderAPI expansion (identifier: "ml")
│ └── PlaceholderCommand.java # Execute commands with placeholder replacement
└── utils/
├── ConfigStrings.java # All constants: permissions, config keys, message keys, IDs
├── CustomMessages.java # Localization wrapper (extends CrucialLib LocalizedFromYaml)
├── SenderUtils.java # Permission checks, message sending, debug logging
├── MathUtils.java # randomDouble(min, max) utility
└── Crucial.java # CrucialLib auto-download and version checking
src/main/resources/
├── plugin.yml # Plugin manifest (uses Maven resource filtering)
└── messages.yml # Empty template; defaults set in MessagesYaml.java
All data is stored as JSON files using CrucialLib's Json utility (GSON-based).
| File | Path | Contents |
|---|---|---|
| Player data | plugins/MiningLevels/data/players.json |
Array of MiningPlayer objects (UUID, level ordinal, XP, unclaimed rewards) |
| Level config | plugins/MiningLevels/config/levels.json |
Array of MiningLevel objects (name, nextLevelXP, skills, commands, rewards) |
| Block config | plugins/MiningLevels/config/blocks.json |
Array of MiningBlock objects (materials list, XP, minLevel) |
Level and block JSON files are auto-downloaded from Google Drive on first run if missing. Player data is saved on player quit and plugin disable.
| Key | Default | Description |
|---|---|---|
levelup_sound |
ENTITY_PLAYER_LEVELUP |
Sound played on level up (any Bukkit Sound enum name) |
max_level_xp_drops |
false |
Whether players continue earning XP at max level |
level_with.player_placed_blocks |
false |
Whether player-placed blocks grant XP |
level_with.generated_blocks |
false |
Whether world-generated blocks (e.g., cobblestone generators) grant XP |
level_progression_messages |
actionBar |
How progress is displayed: actionBar, chat, title, or bossBar |
destroy_mining_blocks_on_explode |
true |
Whether TNT can destroy blocks above the igniter's level |
mining_items |
All 6 pickaxe types | List of Material names that count as mining tools for XP |
prefix |
§8[§6ML§8] §r |
Chat message prefix |
language |
CUSTOM |
Localization language identifier |
admin.debug |
false |
Enable debug messages for players with mininglevels.debug |
| Subcommand | Arguments | Permission | Console | Description |
|---|---|---|---|---|
| (none) | — | — | No | Opens player's own profile GUI |
help |
— | — | Yes | Shows available commands |
info |
— | — | Yes | Shows plugin name, version, author |
self |
— | — | No | Opens own profile GUI |
level |
<player> |
mininglevels.level |
Yes | Query a player's current level |
leaderboard |
— | mininglevels.leaderboard |
No | Opens leaderboard GUI (console gets text output) |
setlevel |
<player> <level> |
mininglevels.setlevel |
Yes | Set a player's mining level |
setxp |
<player> <xp> |
mininglevels.setxp |
Yes | Set a player's XP (triggers level-up check) |
reload |
— | mininglevels.reload |
Yes | Reload config, levels, blocks, and messages |
leveleditor |
— | mininglevels.editor |
No | Opens level management GUI |
blockeditor |
— | mininglevels.editor |
No | Opens block management GUI |
No arguments. Claims all pending level-up rewards into the player's inventory. Not usable from console.
| Permission | Description |
|---|---|
mininglevels.* |
Grants all permissions (admin wildcard) |
mininglevels.level |
Query other players' levels |
mininglevels.setlevel |
Set players' levels |
mininglevels.setxp |
Set players' XP |
mininglevels.leaderboard |
Access leaderboard |
mininglevels.reload |
Reload plugin configuration and data |
mininglevels.editor |
Access level and block editor GUIs |
mininglevels.debug |
See debug messages (also requires admin.debug: true in config) |
Operators and players with mininglevels.* bypass all permission checks.
- Player damages a block →
MiningEvents.onBlockDamage()checks if it's a registeredMiningBlock - If player's level < block's
minLevel, the event is cancelled and a level-needed message is shown - If player has sufficient level and holds a configured mining item:
- Haste potion effect is applied (5 seconds, amplifier = level's
hasteLevel) - Instant break chance is rolled against
instantBreakProbability
- Haste potion effect is applied (5 seconds, amplifier = level's
- Player breaks the block →
MiningEvents.onBlockBreak():- Verifies the block isn't in the
noXpBlockslist (player-placed or generated blocks) - Awards XP via
miningPlayer.alterXp(block.getXp())after a 2-tick delay - Rolls extra ore chance against
extraOreProbability, drops 1 tomaxExtraOrebonus items
- Verifies the block isn't in the
- Deducts current level's
nextLevelXPfrom player's XP - Sets player to the next level
- Plays configured sound
- Displays skill change messages (haste, instant break, extra ore, max extra ore) if values differ
- Lists reward items in chat and adds them to
unclaimedRewards - Executes configured commands (requires PlaceholderAPI)
If XP goes below 0, the player drops a level and XP wraps to previousLevel.nextLevelXP + currentXP.
| Skill | Type | Range | Effect |
|---|---|---|---|
hasteLevel |
int | 0+ | Haste potion effect amplifier when mining |
instantBreakProbability |
float | 0–100 | % chance to instantly break a mining block |
extraOreProbability |
float | 0–100 | % chance to receive bonus ore drops |
maxExtraOre |
int | 0+ | Maximum number of bonus ore items dropped |
- Player-placed blocks: When
level_with.player_placed_blocksisfalse, placed mining blocks are tracked and won't grant XP - Generated blocks: When
level_with.generated_blocksisfalse, blocks formed by world events (e.g., cobblestone generators) are tracked and won't grant XP - TNT explosions: When
destroy_mining_blocks_on_explodeisfalse, blocks requiring a level higher than the TNT igniter's level are removed from the explosion block list
Expansion identifier: ml. All placeholders use the format %ml_<key>%.
| Placeholder | Returns |
|---|---|
%ml_player_name% |
Player's username |
%ml_level% |
Current level display name |
%ml_xp% |
Current XP amount |
%ml_xp_needed% |
XP required for next level |
%ml_xp_percent% |
XP progress as integer percentage (0–100) |
%ml_extra_ore_probability% |
Current extra ore probability |
%ml_max_extra_ore% |
Current max extra ore |
%ml_instant_break_probability% |
Current instant break probability |
%ml_haste_level% |
Current haste level |
%ml_rank% |
Player's leaderboard rank |
Format: %ml_rank_<N>_<field>% where N is 1-indexed rank position.
| Placeholder | Returns |
|---|---|
%ml_rank_<N>_name% |
Nth ranked player's name |
%ml_rank_<N>_level% |
Nth ranked player's level name |
%ml_rank_<N>_xp% |
Nth ranked player's XP |
%ml_rank_<N>_xpNeeded% |
Nth ranked player's next level XP |
%ml_rank_<N>_xpPercent% |
Nth ranked player's XP progress % |
Levels can define commands executed on level-up (requires PlaceholderAPI). Two prefixes are supported:
console:<command>— Runs as console (e.g.,console:give %player_name% diamond 1)player:<command>— Runs as the player (e.g.,player:msg admin I leveled up!)
All PlaceholderAPI placeholders are resolved before execution.
All user-facing messages are localizable via messages.yml. Messages support {0}, {1}, etc. positional parameters. Default values are set in MessagesYaml.java and written to the file on first run.
Key messages: no_permission, new_level, player_not_exist, xp_received, level_of, level_unlocked, hastelvl_change, instant_break_change, extra_ore_change, max_extra_ore_change, rewards_list, claim_your_reward, level_dropped, xp_gained, reload_successful, error_occurred, no_console_command, current_level, current_xp, current_haste_level, current_instant_break_level, current_extra_ore_level, current_max_extra_ore, only_blocks_allowed, cant_delete_level, no_rewards, rewards_claimed, no_more_space, level_needed, leaderboard_header, usage_set_level, usage_set_xp, usage_level, close.
- Main class:
MiningLevels.java— handles lifecycle (onLoad/onEnable/onDisable), config defaults, and event/command registration - Static collections:
MiningPlayer.miningPlayers,MiningLevel.miningLevels, andMiningBlock.miningBlocksare in-memoryArrayLists that serve as the data store - GUI framework: All GUIs extend
Pagefrom CrucialLib, providing inventory-based UIs - Localization: Uses CrucialLib's
Localizerwith the identifiermininglevels - Serialization: GSON via CrucialLib's
Jsonutility;RewardwrapsItemStackfor JSON compatibility - Permission bypass:
SenderUtils.hasOnePermissions()grants access to ops and players withmininglevels.* - bStats ID: 14709 (anonymous usage statistics)
- Spigot Resource ID: 100886