Enhance ability validation: add method to check if an ability can be resolved without deep copy and prune invalid unlocked abilities during load

This commit is contained in:
Kam committed 2026-02-16 01:39:14 -05:00
1 parent 1ed012331b
commit cd28f69a0e
2 files changed
+54

No files matched your search

@@ -340,6 +340,37 @@ public class AbilityController implements IAbilityHandler {
return builtAbilities.containsKey(key) || customAbilities.containsKey(key);
}
/**
* Check if a key can be resolved to a valid ability without creating a deep copy.
* Uses the same lookup chain as {@link #resolveAbility(String)}.
*/
public boolean canResolveAbility(String key) {
if (key == null || key.isEmpty()) return false;
// Built-in: exact name
if (builtAbilities.containsKey(key)) return true;
// Built-in: case-insensitive name
for (String name : builtAbilities.keySet()) {
if (name.equalsIgnoreCase(key)) return true;
}
// Built-in: registry key / ID
for (Ability ability : builtAbilities.values()) {
if (ability.getId() != null && ability.getId().equalsIgnoreCase(key)) return true;
}
// Custom: exact UUID
if (customAbilities.containsKey(key)) return true;
// Custom: case-insensitive name
for (Ability ability : customAbilities.values()) {
if (ability.getName() != null && ability.getName().equalsIgnoreCase(key)) return true;
}
return false;
}
// ═══════════════════════════════════════════════════════════════════════════
// EXTENSION POINTS
// ═══════════════════════════════════════════════════════════════════════════
@@ -15,6 +15,7 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.util.DamageSource;
import noppes.npcs.LogWriter;
import noppes.npcs.api.ability.IPlayerAbilityData;
import noppes.npcs.controllers.AnimationController;
import noppes.npcs.controllers.data.Animation;
@@ -23,6 +24,7 @@ import kamkeel.npcs.network.packets.data.ability.PlayerAbilityStatePacket;
import kamkeel.npcs.network.packets.data.ability.PlayerAbilitySyncPacket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
@@ -860,9 +862,30 @@ public class PlayerAbilityData implements IPlayerAbilityData {
}
}
selectedIndex = compound.getInteger("PlayerAbilitySelected");
// Prune abilities that no longer exist (deleted custom abilities or removed built-ins)
validateUnlockedAbilities();
if (selectedIndex >= unlockedAbilities.size()) {
selectedIndex = Math.max(0, unlockedAbilities.size() - 1);
}
playingAbilityAnimation = compound.getBoolean("AbilityAnimating");
}
/**
* Remove any unlocked ability keys that can no longer be resolved.
* Called during load to clean up references to deleted abilities.
*/
private void validateUnlockedAbilities() {
if (AbilityController.Instance == null) return;
Iterator<String> it = unlockedAbilities.iterator();
while (it.hasNext()) {
String key = it.next();
if (!AbilityController.Instance.canResolveAbility(key)) {
it.remove();
LogWriter.info("Removed invalid ability reference from player data: " + key);
}
}
}
}