Fix Salvage yielding nothing for damaged items with low max quantity
Salvage.calculateSalvageableAmount floored the salvageable amount to zero for any damaged item when the configured maximum quantity was 1, so such items could only be salvaged at full durability. Damaged items now salvage at least one material unless fully broken.
Fix skill scoreboards not updating for child skills
XP and level events only fire for parent skills, so a kept Salvage or Smelting sidebar never matched the event skill and never refreshed until the command was run again. Boards displaying a child skill now refresh when one of its parent skills changes.
Fix power level tags dropping updates and leftover objectives
The power level heartbeat cleared all pending refreshes even when a player's profile had not loaded yet, leaving their below-name tag at 0 until the next level-up. Pending names now stay queued until they can be resolved. Also removes a persisted mcmmo_pwrlvl objective left in world scoreboard data by older versions when the packet or noop backend is active, which otherwise kept rendering stale tags.
Fix localized skill commands being blocked by command whitelist plugins
The locale alias rewrite ran at LOWEST priority, tying with command whitelist plugins listening on the same event, so which name they saw depended on plugin load order. The rewrite now runs at LOW priority and ignores cancelled events, letting whitelist plugins deterministically see the command exactly as the player typed it.
Fix salvage and repair confirmations acting on the wrong item
The confirmation window was time based only, so armor swapped into the hand by a mis-click could be salvaged with no new prompt, including worn armor via the vanilla quick-swap. Confirmations are now bound to the prompted item and re-prompt when the held item changes. Also denies item use while a confirmation is pending, closing the path where the follow-up use-item interact event equipped the armor instead when the server's re-raytrace missed the anvil.
Fix stats scoreboard power level counting permission-hidden skills
The stats sidebar summed every skill into its power level line while hiding rows for skills the player lacked permission for, disagreeing with chat stats and the power level cap, which both use the permission-aware total.
The SQL purge compared a seconds-based last login delta against a milliseconds cutoff, requiring roughly 83 years of inactivity per configured month. The flatfile purge had an inverted check that only removed users with an unknown last login while keeping genuinely inactive ones.
Covers the UserManager shutdown null guard (#4131), legacy color codes leaking into level-up notification components (#5125), the Green Thumb probability guarantee at max bonus level (#4365), and dynamic subskill chance caps for the levels reported against Dodge (#5210). (commit: d171e20)
Add config option to limit Blast Mining remote detonation distance
Server admins can now set Skills.Mining.BlastMining.RemoteDetonationDistance in advanced.yml to control how far away players can remotely detonate TNT. Defaults to 100, matching the previous hardcoded limit, and is added to existing advanced.yml files automatically on startup. Values below 1 fail config validation.
Make /inspect on offline players respect inspect permissions
Inspecting an offline player previously skipped the permission checks that gate inspecting distant online players. /inspect now requires mcmmo.commands.inspect.far (default: op) to view players who are offline, and vanished players hidden from the sender are gated the same way so they stay indistinguishable from offline players. Grant mcmmo.commands.inspect.far to any ranks that should keep inspecting offline players.
Fix ActionBarNotifications.SubSkillUnlocked settings being ignored
Skill rank unlock messages were always sent to chat, ignoring the Feedback.ActionBarNotifications.SubSkillUnlocked settings in advanced.yml. Unlock notifications now use the same routing as other notification types: setting Feedback.ActionBarNotifications.SubSkillUnlocked.Enabled to true sends them to the action bar, and Feedback.ActionBarNotifications.SubSkillUnlocked.SendCopyOfMessageToChat controls whether the chat still receives a copy. The shipped default for Enabled is now false so unlock messages stay in chat out of the box. These notifications also fire McMMOPlayerNotificationEvent, matching the other notification types.
Add /mclevelupsound to toggle the level-up sound per player
Players can now silence the mcMMO level-up sound for themselves with /mclevelupsound (alias /levelupsound) without touching notifications or the server-wide General.LevelUp_Sounds config setting. The toggle also mutes the party level-up sound for that player.
Permission node: mcmmo.commands.mclevelupsound (granted by mcmmo.commands.defaults). Like /mcnotify, the preference is per-session and resets to on at login.
The XP rate argument now accepts positive decimal numbers such as 1.5 instead of integers only. Zero, negative, and non-finite rates are rejected, and an invalid rate no longer toggles the XP event state. Whole-number rates still display without a trailing .0 in broadcasts and admin notifications.
Running /xprate with no arguments now tells the sender the current global XP rate. Access is controlled by the new permission node mcmmo.commands.xprate.show, granted to players by default through mcmmo.commands.defaults since the rate is already broadcast publicly when events start and shown on login.
Fix /xprate rate display using a shared DecimalFormat across threads
The formatted XP rate came from a single static DecimalFormat, which is not thread safe. Regionized servers like Folia execute player commands on multiple region threads, and every player can run /xprate to view the rate, so simultaneous use could garble the broadcast or displayed rate. The format is now created per call, matching how locale strings already build a fresh MessageFormat per message. (commit: bc023b1)
Mention viewing the current rate in the /xprate command descriptions
The /xprate description in plugin.yml and the Commands.Description.xprate locale entry only mentioned modifying the rate, but the command now also shows the current XP rate when run with no arguments. (commit: 3c6ab95)
The Salvage note used wooden tools as an example of items that salvage into a single material, but most wooden tools return more than one material in the default salvage.vanilla.yml; wooden shovels are the accurate example. Also moves the skillranks.yml removal line ahead of the locale entries to match the usual entry ordering, and documents the updated /xprate locale entries, the rank unlock notifications now firing McMMOPlayerNotificationEvent, and the new regression tests. (commit: 9aa347b)
Fix power level tag re-marks being lost during a heartbeat write
The heartbeat removed a pending name after resolving and writing it, so a level-up that re-marked the same player mid-write was erased along with the processed entry, leaving the below-name tag stale until the player's next level-up. Names are now removed before resolving so a concurrent re-mark always survives to the next pass. (commit: 295647a)
Fix inspect stats boards never refreshing on the watched player's level up
The otherboard scan in handleLevelUp found stats boards inspecting the leveling player but then checked and refreshed the leveling player's own board instead of the matching one, so an inspect board kept showing stale levels until re-run while the watched player's board could be redrawn spuriously. The scan now refreshes the board it matched. (commit: 1f28520)
Fix stats scoreboard power level line vanishing for full-permission players
The stats sidebar appended the power level line after sorting the skill rows, so once 15 or more skill rows were shown (a player permitted all 17 skills) the line cap truncated it away entirely. The line now takes part in the sort; the sum is never smaller than any single row, so it ranks first and always survives truncation, matching the old score-sorted Bukkit board where the highest score displayed on top. (commit: c41387b)
Fix anvil confirmation item-use denial never applying to right-click air
The deny guard ran inside onPlayerInteractLowest, which is registered with ignoreCancelled. A PlayerInteractEvent with no clicked block reports itself as cancelled from the moment it is constructed, so the follow-up use-item interaction that equips armor mid-confirmation never reached the guard. Clicking the anvil could also override the denial, because the use-item allowance for anvil blocks is applied after it.
The guard now runs in its own handler at LOW priority without ignoring cancelled events, so it sees right-click-air interactions and takes precedence over the anvil use-item allowance. The listener tests now dispatch through the interact handlers honoring their real EventHandler settings, which is what exposed the dead path. (commit: 95babed)
Fix repair confirmations re-prompting after every partial repair
Repair confirmations are bound to the prompted item, but a repair changes the item's damage, so the pending confirmation stopped matching as soon as the repair completed. Partial repairs are the norm, so continuing to repair the same item re-prompted after every repair instead of only when the held item actually changed.
A matching confirmation is now rebound to the item's repaired state, so repairing the same item within the confirmation window keeps going without new prompts. This also keeps the use-item denial covering the just-repaired item, which unlike salvage stays in the player's hand. (commit: 53b16a5)
Fix database purge deleting flatfile users with unreadable last login
An unparseable last-login value was treated as a real timestamp of 0, so the user looked ancient and was removed even though their last login could not be determined. Unreadable values are now treated like an unknown last login: the purge tries to recover a real timestamp from the server's offline player data and keeps the user otherwise. (commit: 3885c1c)
Fix powerless user purge deleting comments from the flatfile database
Purging powerless users treated every line in mcmmo.users as a user, so the generated comment header had no skill data, counted as a powerless user, and was removed while inflating the purged total. Comments and empty lines are now preserved like the other file rewrites. (commit: 8e2a45d)