diff --git a/src/api b/src/api index 3d648425f..22bce4b2a 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 3d648425fabfd28ba74fc9743e6c3c19609d90bc +Subproject commit 22bce4b2acc9cf9a120a7fdddbb62cc4e567c796 diff --git a/src/main/java/kamkeel/npcs/command/AbilityCommand.java b/src/main/java/kamkeel/npcs/command/AbilityCommand.java index 90d20ea1e..10df3cdbe 100644 --- a/src/main/java/kamkeel/npcs/command/AbilityCommand.java +++ b/src/main/java/kamkeel/npcs/command/AbilityCommand.java @@ -33,20 +33,19 @@ public class AbilityCommand extends CommandKamkeelBase { desc = "List all custom abilities" ) public void list(ICommandSender sender, String[] args) { - Set uuids = AbilityController.Instance.getCustomAbilityIds(); - if (uuids.isEmpty()) { + Set names = AbilityController.Instance.getCustomAbilityNames(); + if (names.isEmpty()) { sendResult(sender, "No custom abilities found."); return; } - sendResult(sender, "Custom Abilities (" + uuids.size() + "):"); - for (String uuid : uuids) { - Ability ability = AbilityController.Instance.getCustomAbility(uuid); + sendResult(sender, "Custom Abilities (" + names.size() + "):"); + for (String name : names) { + Ability ability = AbilityController.Instance.getCustomAbility(name); if (ability != null) { - String name = ability.getName() != null ? ability.getName() : uuid; - sendResult(sender, " - \u00A7b" + name + "\u00A77 (" + ability.getTypeId() + ") [" + uuid + "]"); + sendResult(sender, " - \u00A7b" + name + "\u00A77 (" + ability.getTypeId() + ")"); } else { - sendResult(sender, " - \u00A7b" + uuid); + sendResult(sender, " - \u00A7b" + name); } } } @@ -82,23 +81,23 @@ public class AbilityCommand extends CommandKamkeelBase { } @SubCommand( - desc = "Delete a custom ability by UUID", - usage = "" + desc = "Delete a custom ability by name", + usage = "" ) public void delete(ICommandSender sender, String[] args) { if (args.length < 1) { - sendError(sender, "Usage: /kam ability delete "); + sendError(sender, "Usage: /kam ability delete "); return; } - String uuid = args[0]; - if (!AbilityController.Instance.hasCustomAbility(uuid)) { - sendError(sender, "No custom ability with UUID: " + uuid); + String name = joinArgs(args, 0); + if (!AbilityController.Instance.hasCustomAbilityName(name)) { + sendError(sender, "No custom ability with name: " + name); return; } - AbilityController.Instance.deleteCustomAbility(uuid); - sendResult(sender, "Deleted ability: \u00A7b" + uuid); + AbilityController.Instance.deleteCustomAbility(name); + sendResult(sender, "Deleted ability: \u00A7b" + name); } @SubCommand( @@ -189,7 +188,7 @@ public class AbilityCommand extends CommandKamkeelBase { return; } - // Use the ability's canonical ID for storage (registry key for built-in, UUID for custom) + // Use the ability's canonical ID for storage (registry key for built-in, name for custom) String canonicalKey = ability.getId(); if (canonicalKey == null || canonicalKey.isEmpty()) { canonicalKey = abilityKey; // Fallback to user-provided key diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java index c4fe3b8c0..406d66c12 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilityController.java @@ -24,6 +24,7 @@ import java.io.File; import java.util.*; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.regex.Pattern; public class AbilityController implements IAbilityHandler { @@ -40,6 +41,11 @@ public class AbilityController implements IAbilityHandler { private final List extenders = new ArrayList<>(); private final List> flightCheckers = new ArrayList<>(); + // ── Legacy Migration ───────────────────────────────────────────────────── + private static final Pattern UUID_PATTERN = + Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", + Pattern.CASE_INSENSITIVE); + // ── Derived State ──────────────────────────────────────────────────────── private final Set builtInTypeIds = new HashSet<>(); @@ -156,6 +162,7 @@ public class AbilityController implements IAbilityHandler { public void load() { customAbilities.clear(); + int migrated = 0; File dir = getDir(); File[] files = dir.exists() ? dir.listFiles() : null; @@ -164,14 +171,39 @@ public class AbilityController implements IAbilityHandler { if (!file.isFile() || !file.getName().endsWith(".json")) continue; try { String filename = file.getName(); - String uuid = filename.substring(0, filename.length() - 5); + String key = filename.substring(0, filename.length() - 5); NBTTagCompound nbt = NBTJsonUtil.LoadFile(file); Ability ability = fromNBT(nbt); - if (ability != null) { - ability.setId(uuid); - customAbilities.put(uuid, ability); + if (ability == null) continue; + + // Legacy migration: UUID-named files get renamed to name-based + if (UUID_PATTERN.matcher(key).matches()) { + String name = ability.getName(); + if (name == null || name.isEmpty()) { + name = key; // Fallback to UUID if no name stored + } + + // Ensure unique name + while (hasCustomAbilityName(name) || nameFileExists(dir, name)) { + name = name + "_"; + } + ability.setName(name); + + // Rename file from UUID.json to name.json + File newFile = new File(dir, name + ".json"); + if (file.renameTo(newFile)) { + LogWriter.info("Migrated ability file: " + key + ".json -> " + name + ".json"); + } else { + LogWriter.error("Failed to rename ability file: " + key + ".json -> " + name + ".json"); + } + + key = name; + migrated++; } + + ability.setId(key); + customAbilities.put(key, ability); } catch (Exception e) { LogWriter.error("Error loading custom ability: " + file.getAbsolutePath(), e); } @@ -179,21 +211,57 @@ public class AbilityController implements IAbilityHandler { } customAbilityRevision++; + if (migrated > 0) { + LogWriter.info("Migrated " + migrated + " UUID-named abilities to name-based files"); + } LogWriter.info("Loaded " + customAbilities.size() + " custom abilities"); } + private boolean nameFileExists(File dir, String name) { + return new File(dir, name + ".json").exists(); + } + public boolean saveCustomAbility(Ability ability) { if (ability == null) return false; - String uuid = ability.getId(); - if (uuid == null || uuid.isEmpty()) { - uuid = UUID.randomUUID().toString(); - ability.setId(uuid); + String name = ability.getName(); + if (name == null || name.isEmpty()) return false; + + // Ensure unique name for new abilities (no existing id) + String oldName = ability.getId(); + boolean isNew = oldName == null || oldName.isEmpty(); + if (isNew) { + while (hasCustomAbilityName(name)) { + name = name + "_"; + } + ability.setName(name); } + // Handle rename: old id (previous name) differs from current name + if (!isNew && !oldName.equals(name)) { + // Check uniqueness of new name (excluding the entry being renamed) + String testName = name; + while (customAbilities.containsKey(testName) && !testName.equals(oldName)) { + testName = testName + "_"; + } + if (!testName.equals(name)) { + name = testName; + ability.setName(name); + } + + // Delete old file and remove old map entry + File oldFile = new File(getDir(), oldName + ".json"); + if (oldFile.exists()) { + oldFile.delete(); + } + customAbilities.remove(oldName); + } + + ability.setId(name); + File dir = getDir(); - File fileNew = new File(dir, uuid + ".json_new"); - File fileCurrent = new File(dir, uuid + ".json"); + File fileNew = new File(dir, name + ".json_new"); + File fileCurrent = new File(dir, name + ".json"); try { NBTTagCompound nbt = ability.writeNBT(); @@ -207,52 +275,43 @@ public class AbilityController implements IAbilityHandler { fileNew.delete(); } - customAbilities.put(uuid, ability); + customAbilities.put(name, ability); customAbilityRevision++; - LogWriter.info("Saved custom ability: " + ability.getName() + " [" + uuid + "]"); + LogWriter.info("Saved custom ability: " + name); SyncController.syncAllCustomAbilities(); return true; } catch (Exception e) { - LogWriter.error("Error saving custom ability: " + uuid, e); + LogWriter.error("Error saving custom ability: " + name, e); return false; } } - public boolean deleteCustomAbility(String uuid) { - if (uuid == null || uuid.isEmpty()) return false; + public boolean deleteCustomAbility(String name) { + if (name == null || name.isEmpty()) return false; - Ability removed = customAbilities.remove(uuid); + Ability removed = customAbilities.remove(name); if (removed == null) return false; File dir = getDir(); - File file = new File(dir, uuid + ".json"); + File file = new File(dir, name + ".json"); if (file.exists()) { file.delete(); } customAbilityRevision++; - LogWriter.info("Deleted custom ability: " + uuid); + LogWriter.info("Deleted custom ability: " + name); SyncController.syncAllCustomAbilities(); return true; } - public Ability getCustomAbility(String uuid) { - return customAbilities.get(uuid); + public Ability getCustomAbility(String name) { + return customAbilities.get(name); } - public boolean hasCustomAbility(String uuid) { - return customAbilities.containsKey(uuid); - } - - public Set getCustomAbilityIds() { + public Set getCustomAbilityNames() { return new LinkedHashSet<>(customAbilities.keySet()); } - public String getCustomAbilityName(String uuid) { - Ability a = customAbilities.get(uuid); - return a != null ? a.getName() : null; - } - public Map getCustomAbilities() { return customAbilities; } @@ -300,7 +359,7 @@ public class AbilityController implements IAbilityHandler { } } - // Custom: exact UUID + // Custom: exact name Ability custom = customAbilities.get(key); if (custom != null) return custom.deepCopy(); @@ -322,7 +381,7 @@ public class AbilityController implements IAbilityHandler { keys.add(id); } } - // Use ability names for custom abilities (instead of UUIDs) for readable tab completion + // Custom abilities keyed by name for (Ability ability : customAbilities.values()) { String name = ability.getName(); if (name != null && !name.isEmpty()) { @@ -342,7 +401,7 @@ public class AbilityController implements IAbilityHandler { } } } - // Use ability names for custom abilities (instead of UUIDs) for readable tab completion + // Custom abilities keyed by name for (Ability ability : customAbilities.values()) { if (ability.getAllowedBy().allowsPlayer()) { String name = ability.getName(); @@ -378,7 +437,7 @@ public class AbilityController implements IAbilityHandler { if (ability.getId() != null && ability.getId().equalsIgnoreCase(key)) return true; } - // Custom: exact UUID + // Custom: exact name if (customAbilities.containsKey(key)) return true; // Custom: case-insensitive name @@ -549,17 +608,17 @@ public class AbilityController implements IAbilityHandler { } @Override - public String[] getCustomAbilityIdArray() { + public String[] getCustomAbilityNameArray() { return customAbilities.keySet().toArray(new String[0]); } @Override - public boolean hasCustomAbilityId(String uuid) { - return customAbilities.containsKey(uuid); + public boolean hasCustomAbilityName(String name) { + return customAbilities.containsKey(name); } @Override - public boolean deleteCustomAbilityById(String uuid) { - return deleteCustomAbility(uuid); + public boolean deleteCustomAbilityByName(String name) { + return deleteCustomAbility(name); } } diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java index 4849e4dd4..ab8446114 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/AbilitySlot.java @@ -33,7 +33,7 @@ public class AbilitySlot { } /** - * Create a slot that references a built-in ability (by name) or custom ability (by UUID). + * Create a slot that references a built-in ability (by name) or custom ability (by name). */ public static AbilitySlot reference(String referenceId) { AbilitySlot slot = new AbilitySlot(); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityGetPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityGetPacket.java index d95051a66..a4757a20c 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityGetPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityGetPacket.java @@ -20,18 +20,18 @@ import net.minecraft.nbt.NBTTagCompound; import java.io.IOException; /** - * Request packet to get a specific custom ability by UUID. + * Request packet to get a specific custom ability by name. */ public final class CustomAbilityGetPacket extends AbstractPacket { public static String packetName = "Request|CustomAbilityGet"; - private String uuid; + private String name; public CustomAbilityGetPacket() { } - public CustomAbilityGetPacket(String uuid) { - this.uuid = uuid; + public CustomAbilityGetPacket(String name) { + this.name = name; } @Override @@ -47,7 +47,7 @@ public final class CustomAbilityGetPacket extends AbstractPacket { @SideOnly(Side.CLIENT) @Override public void sendData(ByteBuf out) throws IOException { - ByteBufUtils.writeString(out, uuid); + ByteBufUtils.writeString(out, name); } @Override @@ -58,12 +58,10 @@ public final class CustomAbilityGetPacket extends AbstractPacket { if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player)) return; - String id = ByteBufUtils.readString(in); - Ability ability = AbilityController.Instance.getCustomAbility(id); + String abilityName = ByteBufUtils.readString(in); + Ability ability = AbilityController.Instance.getCustomAbility(abilityName); if (ability != null) { NBTTagCompound compound = ability.writeNBT(); - // Include the UUID so the client can track it - compound.setString("_uuid", id); GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); } } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityRemovePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityRemovePacket.java index 3ef040700..487d5f346 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityRemovePacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilityRemovePacket.java @@ -19,18 +19,18 @@ import noppes.npcs.NoppesUtilServer; import java.io.IOException; /** - * Request packet to remove a custom ability by UUID. + * Request packet to remove a custom ability by name. */ public final class CustomAbilityRemovePacket extends AbstractPacket { public static String packetName = "Request|CustomAbilityRemove"; - private String uuid; + private String name; public CustomAbilityRemovePacket() { } - public CustomAbilityRemovePacket(String uuid) { - this.uuid = uuid; + public CustomAbilityRemovePacket(String name) { + this.name = name; } @Override @@ -51,7 +51,7 @@ public final class CustomAbilityRemovePacket extends AbstractPacket { @SideOnly(Side.CLIENT) @Override public void sendData(ByteBuf out) throws IOException { - ByteBufUtils.writeString(out, uuid); + ByteBufUtils.writeString(out, name); } @Override diff --git a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitySavePacket.java b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitySavePacket.java index d5ca1c556..f42ae5ada 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitySavePacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/ability/CustomAbilitySavePacket.java @@ -71,7 +71,6 @@ public final class CustomAbilitySavePacket extends AbstractPacket { AbilityController.Instance.saveCustomAbility(ability); NoppesUtilServer.sendCustomAbilitiesData((EntityPlayerMP) player); NBTTagCompound responseNbt = ability.writeNBT(); - responseNbt.setString("_uuid", ability.getId()); GuiDataPacket.sendGuiData((EntityPlayerMP) player, responseNbt); } } diff --git a/src/main/java/noppes/npcs/DataAbilities.java b/src/main/java/noppes/npcs/DataAbilities.java index f71af949d..22f7100ae 100644 --- a/src/main/java/noppes/npcs/DataAbilities.java +++ b/src/main/java/noppes/npcs/DataAbilities.java @@ -950,7 +950,7 @@ public class DataAbilities { abilitySlots.add(AbilitySlot.inline(ability)); } - /** Add a reference ability by key (built-in name or custom UUID). */ + /** Add a reference ability by key (built-in name or custom ability name). */ public void addAbilityReference(String key) { abilitySlots.add(AbilitySlot.reference(key)); } diff --git a/src/main/java/noppes/npcs/NoppesUtilServer.java b/src/main/java/noppes/npcs/NoppesUtilServer.java index 400a56678..bbb19c97d 100644 --- a/src/main/java/noppes/npcs/NoppesUtilServer.java +++ b/src/main/java/noppes/npcs/NoppesUtilServer.java @@ -810,13 +810,10 @@ public class NoppesUtilServer { kamkeel.npcs.controllers.data.ability.AbilityController controller = kamkeel.npcs.controllers.data.ability.AbilityController.Instance; if (controller != null) { int index = 0; - for (String uuid : controller.getCustomAbilityIds()) { - Ability ability = controller.getCustomAbility(uuid); + for (String name : controller.getCustomAbilityNames()) { + Ability ability = controller.getCustomAbility(name); if (ability == null || !ability.getAllowedBy().allowsNpc()) continue; - - String name = controller.getCustomAbilityName(uuid); - if (name == null || name.isEmpty()) name = uuid; - map.put(name + "\t" + uuid, index++); + map.put(name, index++); } } sendScrollData(player, map, EnumScrollData.CUSTOM_ABILITIES); diff --git a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityLoad.java b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityLoad.java index 103d446fc..905faa1f4 100644 --- a/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityLoad.java +++ b/src/main/java/noppes/npcs/client/gui/advanced/SubGuiAbilityLoad.java @@ -21,7 +21,6 @@ import noppes.npcs.constants.EnumScrollData; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.UUID; import java.util.Vector; /** @@ -33,10 +32,8 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL private final SubGuiAbilityConfig parentConfig; private final GuiNPCAbilities parentAbilities; private String selectedDisplayName = null; - private String selectedUuid = null; + private String selectedName = null; - /** Maps display name -> UUID for custom abilities. */ - private final HashMap displayToUuid = new HashMap<>(); private HashMap rawData = new HashMap<>(); private GuiCustomScroll scroll; @@ -97,15 +94,15 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL addButton(new GuiNpcButton(0, guiLeft + 10, y, 95, 20, "gui.load")); addButton(new GuiNpcButton(2, guiLeft + 115, y, 95, 20, "gui.cancel")); - getButton(0).setEnabled(selectedUuid != null); + getButton(0).setEnabled(selectedName != null); } private List getFilteredList() { if (search.isEmpty()) { - return new ArrayList<>(displayToUuid.keySet()); + return new ArrayList<>(rawData.keySet()); } List list = new ArrayList<>(); - for (String name : displayToUuid.keySet()) { + for (String name : rawData.keySet()) { if (name.toLowerCase().contains(search.toLowerCase())) { list.add(name); } @@ -117,7 +114,7 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL public void buttonEvent(GuiButton guibutton) { int id = guibutton.id; - if (id == 0 && selectedUuid != null) { + if (id == 0 && selectedName != null) { if (parentAbilities != null) { // From NPC abilities GUI - ask clone/reference setSubGui(new SubGuiAbilityLoadMode()); @@ -125,7 +122,7 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL // From config GUI - always clone waitingForLoad = true; pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE; - PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid)); + PacketClient.sendClient(new CustomAbilityGetPacket(selectedName)); } } else if (id == 2) { close(); @@ -152,16 +149,16 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL if (mode < 0) return; // cancelled if (mode == SubGuiAbilityLoadMode.MODE_REFERENCE) { - // Reference mode - just pass the reference ID - if (parentAbilities != null && selectedUuid != null) { - parentAbilities.loadAbilityReference(selectedUuid); + // Reference mode - just pass the reference name + if (parentAbilities != null && selectedName != null) { + parentAbilities.loadAbilityReference(selectedName); } close(); } else { // Clone mode - request full data waitingForLoad = true; pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE; - PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid)); + PacketClient.sendClient(new CustomAbilityGetPacket(selectedName)); } } } @@ -170,9 +167,7 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL public void customScrollClicked(int i, int j, int k, GuiCustomScroll scroll) { if (scroll.id == 0) { selectedDisplayName = scroll.getSelected(); - if (selectedDisplayName != null) { - selectedUuid = displayToUuid.get(selectedDisplayName); - } + selectedName = selectedDisplayName; initGui(); } } @@ -181,15 +176,13 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL public void customScrollDoubleClicked(String selection, GuiCustomScroll scroll) { if (scroll.id == 0 && selection != null && !selection.isEmpty()) { selectedDisplayName = selection; - selectedUuid = displayToUuid.get(selectedDisplayName); - if (selectedUuid != null) { - if (parentAbilities != null) { - setSubGui(new SubGuiAbilityLoadMode()); - } else { - waitingForLoad = true; - pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE; - PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid)); - } + selectedName = selection; + if (parentAbilities != null) { + setSubGui(new SubGuiAbilityLoadMode()); + } else { + waitingForLoad = true; + pendingLoadMode = SubGuiAbilityLoadMode.MODE_CLONE; + PacketClient.sendClient(new CustomAbilityGetPacket(selectedName)); } } } @@ -197,22 +190,10 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL @Override public void setData(Vector list, HashMap data, EnumScrollData type) { if (type == EnumScrollData.CUSTOM_ABILITIES) { - displayToUuid.clear(); rawData = data; - for (String key : data.keySet()) { - // key format: "displayName\tUUID" - int tabIndex = key.indexOf('\t'); - if (tabIndex > 0) { - String displayName = key.substring(0, tabIndex); - String uuid = key.substring(tabIndex + 1); - displayToUuid.put(displayName, uuid); - } else { - displayToUuid.put(key, key); - } - } if (scroll != null) { scroll.setList(getFilteredList()); - if (selectedDisplayName != null && displayToUuid.containsKey(selectedDisplayName)) { + if (selectedDisplayName != null && rawData.containsKey(selectedDisplayName)) { scroll.setSelected(selectedDisplayName); } } @@ -226,11 +207,6 @@ public class SubGuiAbilityLoad extends SubGuiInterface implements ICustomScrollL waitingForLoad = false; Ability loaded = AbilityController.Instance.fromNBT(compound); if (loaded != null) { - if (pendingLoadMode == SubGuiAbilityLoadMode.MODE_CLONE) { - // Give it a new UUID so it's a unique inline copy - loaded.setId(UUID.randomUUID().toString()); - } - if (parentConfig != null) { parentConfig.loadAbility(loaded); } else if (parentAbilities != null) { diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java b/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java index 66e8ed46c..d016e7c77 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNpcManageAbilities.java @@ -3,7 +3,6 @@ package noppes.npcs.client.gui.global; import kamkeel.npcs.client.renderer.TelegraphRenderer; import kamkeel.npcs.controllers.data.ability.Ability; import kamkeel.npcs.controllers.data.ability.AbilityController; -import java.util.UUID; import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance; import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.packets.request.ability.BuiltInAbilityGetPacket; @@ -62,10 +61,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface private GuiCustomScroll scroll; private HashMap customData = new HashMap<>(); private HashMap builtInData = new HashMap<>(); - /** Maps display name -> UUID for custom abilities */ - private final HashMap displayToUuid = new HashMap<>(); private String selected = null; - private String selectedUuid = null; private String search = ""; private Ability selectedAbility = null; @@ -117,7 +113,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface // Remove button — only for custom view addButton(new GuiNpcButton(1, guiLeft + 368, guiTop + 30, 45, 20, "gui.remove")); - getButton(1).setEnabled(selected != null && !selected.isEmpty() && displayToUuid.containsKey(selected)); + getButton(1).setEnabled(selected != null && !selected.isEmpty() && customData.containsKey(selected)); } // Edit button — hidden when viewing built-in @@ -178,29 +174,17 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface } private List getSearchList() { - if (showingBuiltIn) { - if (search.isEmpty()) { - return new ArrayList<>(builtInData.keySet()); - } - List list = new ArrayList<>(); - for (String name : builtInData.keySet()) { - if (name.toLowerCase().contains(search.toLowerCase())) { - list.add(name); - } - } - return list; - } else { - if (search.isEmpty()) { - return new ArrayList<>(displayToUuid.keySet()); - } - List list = new ArrayList<>(); - for (String name : displayToUuid.keySet()) { - if (name.toLowerCase().contains(search.toLowerCase())) { - list.add(name); - } - } - return list; + HashMap data = getCurrentData(); + if (search.isEmpty()) { + return new ArrayList<>(data.keySet()); } + List list = new ArrayList<>(); + for (String name : data.keySet()) { + if (name.toLowerCase().contains(search.toLowerCase())) { + list.add(name); + } + } + return list; } @Override @@ -260,7 +244,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface if (id == 10) { showingBuiltIn = !showingBuiltIn; selected = null; - selectedUuid = null; selectedAbility = null; currentIsBuiltIn = false; search = ""; @@ -276,7 +259,7 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface if (id == 2 && !showingBuiltIn) { // Add — open type selection setSubGui(new SubGuiAbilityTypeSelect()); - } else if (id == 1 && !showingBuiltIn && selected != null && selectedUuid != null) { + } else if (id == 1 && !showingBuiltIn && selected != null) { GuiYesNo guiyesno = new GuiYesNo(this, selected, StatCollector.translateToLocal("gui.delete"), 1); displayGuiScreen(guiyesno); } else if (id == 100 && !showingBuiltIn && !currentIsBuiltIn && selectedAbility != null) { @@ -325,17 +308,11 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface selected = newSelection; if (showingBuiltIn) { - // Fetch built-in ability by display name - selectedUuid = null; currentIsBuiltIn = true; PacketClient.sendClient(new BuiltInAbilityGetPacket(selected)); } else { - // Fetch custom ability by UUID - selectedUuid = displayToUuid.get(selected); currentIsBuiltIn = false; - if (selectedUuid != null) { - PacketClient.sendClient(new CustomAbilityGetPacket(selectedUuid)); - } + PacketClient.sendClient(new CustomAbilityGetPacket(selected)); } } } @@ -357,21 +334,9 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface if (type == EnumScrollData.CUSTOM_ABILITIES) { String prevSelected = scroll != null ? scroll.getSelected() : null; this.customData = data; - displayToUuid.clear(); - for (String key : data.keySet()) { - // key format: "displayName\tUUID" - int tabIndex = key.indexOf('\t'); - if (tabIndex > 0) { - String displayName = key.substring(0, tabIndex); - String uuid = key.substring(tabIndex + 1); - displayToUuid.put(displayName, uuid); - } else { - displayToUuid.put(key, key); - } - } if (!showingBuiltIn && scroll != null) { scroll.setList(getSearchList()); - if (prevSelected != null && displayToUuid.containsKey(prevSelected)) { + if (prevSelected != null && customData.containsKey(prevSelected)) { scroll.setSelected(prevSelected); } } @@ -422,7 +387,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface Ability newAbility = AbilityController.Instance.create(pendingTypeId); if (newAbility != null) { variantGui.getVariants().get(idx).apply(newAbility); - newAbility.setId(UUID.randomUUID().toString()); if (hasDuplicateName(newAbility)) { pendingSaveAbility = newAbility; pendingNewCreation = true; @@ -450,7 +414,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface if (variants.size() == 1) { variants.get(0).apply(newAbility); } - newAbility.setId(UUID.randomUUID().toString()); if (hasDuplicateName(newAbility)) { pendingSaveAbility = newAbility; pendingNewCreation = true; @@ -504,7 +467,6 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface private void openConfig(Ability ability) { selectedAbility = ability; selected = ability.getName(); - selectedUuid = ability.getId(); previewExecutor.stop(); setSubGui(ability.createConfigGui(this)); } @@ -512,12 +474,9 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface private boolean hasDuplicateName(Ability ability) { String name = ability.getName(); if (name == null || name.isEmpty()) return false; - for (java.util.Map.Entry entry : displayToUuid.entrySet()) { - if (entry.getKey().equals(name) && !entry.getValue().equals(ability.getId())) { - return true; - } - } - return false; + String oldName = ability.getId(); + // A duplicate exists if the name is already in use and it's not the same ability being edited + return customData.containsKey(name) && !name.equals(oldName); } @Override @@ -525,11 +484,10 @@ public class GuiNpcManageAbilities extends GuiAbilityInterface NoppesUtil.openGUI(player, this); if (!result) return; - if (id == 1 && selected != null && selectedUuid != null) { - PacketClient.sendClient(new CustomAbilityRemovePacket(selectedUuid)); + if (id == 1 && selected != null) { + PacketClient.sendClient(new CustomAbilityRemovePacket(selected)); scroll.clear(); selected = null; - selectedUuid = null; selectedAbility = null; initGui(); } diff --git a/src/main/java/noppes/npcs/scripted/wrapper/ScriptPlayerAbilityData.java b/src/main/java/noppes/npcs/scripted/wrapper/ScriptPlayerAbilityData.java index 42ed3372e..c1de83878 100644 --- a/src/main/java/noppes/npcs/scripted/wrapper/ScriptPlayerAbilityData.java +++ b/src/main/java/noppes/npcs/scripted/wrapper/ScriptPlayerAbilityData.java @@ -36,7 +36,7 @@ public class ScriptPlayerAbilityData implements IPlayerAbilityData { if (!ability.getAllowedBy().allowsPlayer()) { return; // Ability is NPC-only } - // Use canonical key (registry key for built-in, UUID for custom) + // Use canonical key (registry key for built-in, name for custom) String canonicalKey = ability.getId() != null ? ability.getId() : key; data.unlockAbility(canonicalKey); }