Resolve McMMOPlayer once per event across hot listener paths
Event handlers that null-checked UserManager.getPlayer and then called it again (sometimes several times) now resolve the McMMOPlayer a single time and reuse it. Also hoists the NPC-interaction and attacker lookups out of the applyAbilityAoE loop and gives NotificationManager a single-lookup notification eligibility check. (commit: 4a643dc)
Enchantment presence checks now use ItemStack#containsEnchantment instead of cloning ItemMeta twice per check, enchantment-by-name lookups are remembered in a map instead of scanning the registry every call, isSmelted gains a Material overload so furnace extraction stops allocating a throwaway ItemStack, Chimaera Wing checks fetch ItemMeta once, and getRandomEnchantment picks by index instead of shuffling the treasure book's shared enchantment list on every enchanted-book catch. (commit: b495114)
Stop rebuilding block lookup data on hot BlockUtils paths
isPartOfTree reads the block type once instead of snapshotting the BlockState (it runs on every block place and axe-readying raycast), getTransparentBlocks returns a set built once at class load instead of scanning every Material per call, and onEntityChangeBlock reuses the snapshot it already took instead of capturing a second one. (commit: 7e9dd2a)
Lighten placed-block tracking lookups and chunk store memory
HashChunkManager coordinate keys become a record so map lookups on every block check stop boxing through Objects.hash, and BitSetChunkStore lets its BitSet grow on demand instead of pre-allocating the full world height for every chunk, most of which track few or no blocks. (commit: 348ef94)
Reduce map traffic in FormulaManager level lookups
XP-to-next-level caches now use computeIfAbsent instead of a containsKey/put/get triple per lookup, and calculateXPNeeded falls back to the linear formula without recursing when handed an invalid formula type. (commit: a1f43be)
Read container and item metadata once per operation
Container ownership processing resolves the current owner a single time instead of re-reading persistent data (and re-resolving the OfflinePlayer) for the ownership check, the change, and the debug message. ItemMetadataUtils methods fetch ItemMeta and the persistent data value once each, and removing a super ability boost reuses one meta copy and one Efficiency enchantment lookup. (commit: 594b8ab)
Cut per-event work in click, fishing, and command handlers
Inventory clicks return before resolving player data unless the inventory is a furnace or brewing stand, the fishing catch handler reads the caught item type and the boosted XP value once, and localized skill command aliases are looked up from a map built once per locale generation instead of localizing every skill name on every command. LocaleLoader gains a generation counter so the alias map rebuilds after /mcmmoreloadlocale. (commit: 1a1bffd)
Skill chance rolls hit AdvancedConfig getMaximumProbability and getMaxBonusLevel on every RNG check, rebuilding the advanced.yml key string each time; the addresses are now memoized on SubSkillType like the permission nodes, and the values live in EnumMaps reset by loadKeys(). useAttackCooldown, the Archery force multiplier, and the per-event ExploitFix toggles in experience.yml get the same one-read treatment. The retro mode flag is now stored before AdvancedConfig loads since its level scaling reads that flag while validating. (commit: 6d44041)
Drop ModManager and deprecate unused public classes
ModManager has been a fully commented-out file with zero references for years, so it is deleted outright. NMSConstants, NMSVersion, FixSpellingNetheriteUtil, SkillActivationType, and ObjectiveType have no internal references but are public, so they follow the deprecate-first policy and are annotated forRemoval instead of deleted. (commit: 97ba6db)
Clear out dead code and deprecate orphaned public members
Deletes zero-API-surface dead code: the commented-out Flux Mining, custom-damage-metadata, item-share, firework shower, and 2014-era Misc blocks, PlayerLevelUtils commented constructor, the unused private SpawnOrbTask and hasHeadRoom helpers, an unused local in getParticleLocation, and the onCraftItem handler whose body had no remaining effect.
Public members with no remaining callers follow the deprecate-first policy instead: playAbilityDisabledEffect (now a no-op), SkillUtils.calculateLengthDisplayValues, both handleLuckyPerks overloads, getModName, getKeyGetKey, convertPotionConfigName, FORMATTED_SUBSKILL_NAMES, the size-limited spawnItemsFromCollection, and the unused ItemUtils enchantment-check overloads. (commit: 804bc9a)
Finish the deprecation sweep for existing forRemoval batches
The simulateBlockBreak overloads whose javadoc claimed deprecation without the annotation now carry a real @Deprecated(forRemoval), and all internal callers pass FakeBlockBreakEventType.FAKE explicitly. AbilityDisableTask calls the McMMOPlayer overload of callAbilityDeactivateEvent instead of the deprecated Player one it already had the McMMOPlayer for, and CombatUtils loses a duplicate MobHealthbarUtils import. (commit: 8521f93)
Fix Green Terra and Green Thumb ignoring dirt paths
The mossy whitelist still listed grass_path, which stopped existing when Minecraft 1.17 renamed it to dirt_path, so the dirt path conversion that Herbalism.convertGreenTerraBlocks already supports could never trigger. (commit: 7ce3de3)
Fix Super Breaker ignoring the modern copper block family
The pickaxe block whitelist listed the cut copper slabs and stairs under their pre-1.17-release snapshot names (lightly_weathered_*, semi_weathered_*), which were renamed to exposed/weathered/oxidized in 21w05a and have never matched since. The whitelist now carries the current names for the copper block, cut copper, slab, and stair families including waxed variants. (commit: a26bc85)
Add mangrove and cherry logs to the woodcutting drop list
isWoodcuttingDrop categorizes drops for party item sharing but never learned the mangrove and cherry logs; the stripped mangrove log was present while its unstripped form was not. Item sharing is currently disabled at the pickup handler, so this is parity work for when it returns rather than a player-visible fix. (commit: 2fd3e61)
Cull material names that never match and test the registers
twisted_vines_plant was a misspelling of twisting_vines_plant, so twisting vines never got multi-block plant handling; the name is corrected. Entries that cannot match any block or item in either edition are removed: the pre-1.13 wood_/gold_ tool and armor names (their wooden_/golden_ forms stay), copper_blocks, petrified_oak_stairs, smooth_stone_stairs, and the cut sandstone stair variants that never existed.
Bedrock-style identifiers stay on purpose for Bukkit-API servers with Bedrock naming. A new MaterialMapStoreTest reflects over every register and fails on any name that neither resolves to a real Material nor sits on the documented Bedrock-alias or newer-than-test-API allowlists, so future typos and stale names surface immediately. (commit: fdfc70d)
getNetheriteArmor hands out a copy instead of the live register so callers cannot corrupt it, and getNetheriteTools, which nothing calls, is deprecated for removal. (commit: 50d99fd)
Drop compatibility shims obsoleted by the 1.20.5 floor
PotionEffectUtil returns the HASTE and NAUSEA constants directly instead of reflecting over legacy field names (its skipped tautological tests are removed), EnchantmentMapper resolves each enchantment with a single registry lookup through a shared helper instead of per- enchantment legacy alias loops, the glow berries food case joins the Farmers Diet switch it was special-cased around, the bone meal crop check becomes an EnumSet and drops nether wart block (bone meal cannot be used on it, and the old case let it clear placed-block tracking), and armor stand detection compares EntityType instead of strings. Mannequin stays string-matched since the entity type is newer than the oldest supported API. (commit: ccbd598)
ListenerGuards.resolveEligiblePlayer runs the standard handler gates in cheapest-first order (world blacklist, WorldGuard main flag, player data key, loaded profile) and hands back the McMMOPlayer or null. Adopted by the block damage handlers, both fishing handlers, and the item pickup handler, whose preambles matched it verbatim; handlers with side effects in their gates keep their own logic. (commit: af1d82e)
Restructure melee combat dispatch and combat XP calculation
resolveMeleeSkill resolves which skill claims a hit in the historical predicate order and processMeleeCombat dispatches on the result, replacing six copies of the trigger-toggle/permission/process pattern. The spear branch no longer falls through into the sword branch thanks to the resolver being first-match-wins; new parameterized tests lock the routing. processCombatXP splits into isPvpXpIneligible, calculatePvpBaseXP, and calculatePveBaseXP helpers.
The ability preparation branches in BlockListener drop their outer Permissions checks; checkAbilityActivation performs the same check before activating, so the outer copies only duplicated work and one of them (Berserk) sat at the wrong precedence level inside the FISTS condition. (commit: 52b1197)
Deduplicate event firing and hardcore penalty skeletons
EventUtils gains a generic callEvent helper that fires an event through the plugin manager and returns it; the nineteen hand-rolled dispatch lines now go through it. HardcoreManager resolves each McMMOPlayer once, shares a putNoChange helper for the four zero-fill branches, and the two is-enabled-for-any-skill loops collapse into one predicate-based helper. (commit: 5e36895)
Consolidate the XP perk tiers into an ordered table
handleXpPerks resolves the fixed multiplier tiers through a record-backed list checked strongest-first instead of an eight-branch if chain; the custom boost keeps its special handling and every permission node stays exactly as it was. (commit: 7fd0759)
Deduplicate milestone broadcasts in NotificationManager
processLevelUpBroadcasting and processPowerLevelUpBroadcasting shared their audience/hover/send body almost line for line; both now collapse their guards into a single early return and delegate to a shared broadcastMilestone helper. sendNearbyPlayersInformation is deprecated since it never messaged nearby players despite the name (its one internal caller now calls sendPlayerInformation directly), and the misleading could-cache TODO on the per-player broadcast predicate is gone. (commit: 85aa264)