diff --git a/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java b/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java index 03ed7b8..ef30293 100644 --- a/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java +++ b/src/main/java/net/tfminecraft/magic/integration/SkillIdResolver.java @@ -74,22 +74,27 @@ public static SkillHandler handlerForBinding(String skillId) { if (skillId == null || skillId.isBlank() || MMOCore.plugin == null) { return null; } - RegisteredSkill exact = MMOCore.plugin.skillManager.getSkill(skillId); - if (exact != null && exact.getHandler() != null) { - return exact.getHandler(); + // Binding keys are stored lowercase and MythicLib ids are uppercase, so the exact lookup + // needs the upper-cased id. + for (String id : new String[] {skillId, skillId.toUpperCase(Locale.ROOT)}) { + RegisteredSkill exact = MMOCore.plugin.skillManager.getSkill(id); + if (exact != null && exact.getHandler() != null) { + return exact.getHandler(); + } } + // Handler ids before display names: a class copy (CLASS_RESTORATION, shown as "Restoration") + // shares the rune's name, and a name match bound the rune's modifiers to the class skill. for (RegisteredSkill skill : MMOCore.plugin.skillManager.getAll()) { - if (skill == null) { - continue; - } - if (skill.getName() != null && skill.getName().equalsIgnoreCase(skillId)) { - return skill.getHandler(); - } - SkillHandler handler = skill.getHandler(); + SkillHandler handler = skill == null ? null : skill.getHandler(); if (handler != null && skillId.equalsIgnoreCase(handler.getLowerCaseId())) { return handler; } } + for (RegisteredSkill skill : MMOCore.plugin.skillManager.getAll()) { + if (skill != null && skill.getName() != null && skill.getName().equalsIgnoreCase(skillId)) { + return skill.getHandler(); + } + } return null; } } diff --git a/src/test/java/net/tfminecraft/magic/SkillResolverEdgeTest.java b/src/test/java/net/tfminecraft/magic/SkillResolverEdgeTest.java index b24a826..0fc6fea 100644 --- a/src/test/java/net/tfminecraft/magic/SkillResolverEdgeTest.java +++ b/src/test/java/net/tfminecraft/magic/SkillResolverEdgeTest.java @@ -84,4 +84,30 @@ void bindingLookupSupportsNamesAndHandlersCaseInsensitively() throws Exception { assertSame(second, SkillIdResolver.handlerForBinding("ABILITY")); assertNull(SkillIdResolver.handlerForBinding("unknown")); } + + @Test + void bindingPrefersTheRuneHandlerOverAClassCopyWithTheSameName() throws Exception { + MMOCore.plugin = mock(MMOCore.class); + var manager = mock(SkillManager.class); + var field = MMOCore.class.getDeclaredField("skillManager"); + field.setAccessible(true); + field.set(MMOCore.plugin, manager); + // CLASS_RESTORATION is shown as "Restoration" and comes first; the rune RESTORATION has the handler id. + var classCopy = mock(RegisteredSkill.class); + var classHandler = mock(SkillHandler.class); + when(classCopy.getName()).thenReturn("Restoration"); + when(classCopy.getHandler()).thenReturn(classHandler); + when(classHandler.getLowerCaseId()).thenReturn("class_restoration"); + var rune = mock(RegisteredSkill.class); + var runeHandler = mock(SkillHandler.class); + when(rune.getName()).thenReturn("Restoration"); + when(rune.getHandler()).thenReturn(runeHandler); + when(runeHandler.getLowerCaseId()).thenReturn("restoration"); + when(manager.getAll()).thenReturn(List.of(classCopy, rune)); + assertSame(runeHandler, SkillIdResolver.handlerForBinding("restoration")); + // skills.yml keys are stored lowercase; the MythicLib id is upper case + when(manager.getSkill("RESTORATION")).thenReturn(rune); + when(manager.getAll()).thenReturn(List.of()); // only the upper-cased exact lookup can find it now + assertSame(runeHandler, SkillIdResolver.handlerForBinding("restoration")); + } }