diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java b/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java index ff91d1c..a5f8c40 100644 --- a/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java +++ b/api/src/main/java/lol/pyr/znpcsplus/api/npc/Npc.java @@ -1,8 +1,11 @@ package lol.pyr.znpcsplus.api.npc; -import lol.pyr.znpcsplus.api.hologram.Hologram; import lol.pyr.znpcsplus.api.entity.PropertyHolder; +import lol.pyr.znpcsplus.api.hologram.Hologram; + +import java.util.UUID; public interface Npc extends PropertyHolder { Hologram getHologram(); + UUID getUuid(); } diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java b/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java index a5d4a23..fe5d538 100644 --- a/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java +++ b/api/src/main/java/lol/pyr/znpcsplus/api/npc/NpcRegistry.java @@ -4,6 +4,7 @@ import lol.pyr.znpcsplus.util.NpcLocation; import org.bukkit.World; import java.util.Collection; +import java.util.UUID; public interface NpcRegistry { Collection getAll(); @@ -11,6 +12,7 @@ public interface NpcRegistry { Collection getAllPlayerMade(); Collection getAllPlayerMadeIds(); NpcEntry create(String id, World world, NpcType type, NpcLocation location); - NpcEntry get(String id); + NpcEntry getById(String id); + NpcEntry getByUuid(UUID uuid); void delete(String id); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/CreateCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/CreateCommand.java index cf6e4a6..1c3c1e9 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/CreateCommand.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/CreateCommand.java @@ -30,7 +30,7 @@ public class CreateCommand implements CommandHandler { Player player = context.ensureSenderIsPlayer(); String id = context.popString(); - if (npcRegistry.get(id) != null) context.halt(Component.text("NPC with that ID already exists.", NamedTextColor.RED)); + if (npcRegistry.getById(id) != null) context.halt(Component.text("NPC with that ID already exists.", NamedTextColor.RED)); NpcTypeImpl type = context.parse(NpcTypeImpl.class); NpcEntryImpl entry = npcRegistry.create(id, player.getWorld(), type, new NpcLocation(player.getLocation())); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/ListCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/ListCommand.java index 61d7349..a7f9690 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/ListCommand.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/ListCommand.java @@ -22,7 +22,7 @@ public class ListCommand implements CommandHandler { public void run(CommandContext context) throws CommandExecutionException { TextComponent.Builder component = Component.text("Npc List:\n").color(NamedTextColor.GOLD).toBuilder(); for (String id : npcRegistry.getModifiableIds()) { - NpcImpl npc = npcRegistry.get(id).getNpc(); + NpcImpl npc = npcRegistry.getById(id).getNpc(); NpcLocation location = npc.getLocation(); component.append(Component.text("ID: " + id, NamedTextColor.GREEN)) .append(Component.text(" | ", NamedTextColor.GRAY)) diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java index f56619c..9edfebe 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java @@ -40,10 +40,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; +import java.util.*; public class ZNpcImporter implements DataImporter { private final ConfigManager configManager; @@ -102,7 +99,8 @@ public class ZNpcImporter implements DataImporter { ZNpcsLocation oldLoc = model.getLocation(); NpcLocation location = new NpcLocation(oldLoc.getX(), oldLoc.getY(), oldLoc.getZ(), oldLoc.getYaw(), oldLoc.getPitch()); - NpcImpl npc = new NpcImpl(configManager, packetFactory, textSerializer, oldLoc.getWorld(), typeRegistry.getByName(type), location); + UUID uuid = model.getUuid() == null ? UUID.randomUUID() : model.getUuid(); + NpcImpl npc = new NpcImpl(uuid, configManager, packetFactory, textSerializer, oldLoc.getWorld(), typeRegistry.getByName(type), location); HologramImpl hologram = npc.getHologram(); hologram.setOffset(model.getHologramHeight()); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java index 6204502..8ab73a3 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java @@ -2,10 +2,12 @@ package lol.pyr.znpcsplus.conversion.znpcs.model; import java.util.List; import java.util.Map; +import java.util.UUID; @SuppressWarnings("unused") public class ZNpcsModel { private int id; + private UUID uuid; private double hologramHeight; private String skinName; private String glowName; @@ -20,6 +22,10 @@ public class ZNpcsModel { return id; } + public UUID getUuid() { + return uuid; + } + public double getHologramHeight() { return hologramHeight; } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java index 03e309d..f9b97bb 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java @@ -25,19 +25,21 @@ public class NpcImpl extends Viewable implements Npc { private NpcLocation location; private NpcTypeImpl type; private final HologramImpl hologram; + private final UUID uuid; private final Map, Object> propertyMap = new HashMap<>(); private final List actions = new ArrayList<>(); - protected NpcImpl(ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) { - this(configManager, packetFactory, textSerializer, world.getName(), type, location); + protected NpcImpl(UUID uuid, ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) { + this(uuid, configManager, packetFactory, textSerializer, world.getName(), type, location); } - public NpcImpl(ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, String world, NpcTypeImpl type, NpcLocation location) { + public NpcImpl(UUID uuid, ConfigManager configManager, PacketFactory packetFactory, LegacyComponentSerializer textSerializer, String world, NpcTypeImpl type, NpcLocation location) { this.packetFactory = packetFactory; this.worldName = world; this.type = type; this.location = location; + this.uuid = uuid; entity = new PacketEntity(packetFactory, this, type.getType(), location); hologram = new HologramImpl(configManager, packetFactory, textSerializer, location.withY(location.getY() + type.getHologramOffset())); } @@ -76,6 +78,10 @@ public class NpcImpl extends Viewable implements Npc { return hologram; } + public UUID getUuid() { + return uuid; + } + public World getWorld() { return Bukkit.getWorld(worldName); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java index 7e9879f..65dffd9 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcRegistryImpl.java @@ -14,10 +14,7 @@ import lol.pyr.znpcsplus.util.NpcLocation; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.World; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; public class NpcRegistryImpl implements NpcRegistry { @@ -26,7 +23,9 @@ public class NpcRegistryImpl implements NpcRegistry { private final ConfigManager configManager; private final LegacyComponentSerializer textSerializer; - private final Map npcMap = new HashMap<>(); + private final List npcList = new ArrayList<>(); + private final Map npcIdLookupMap = new HashMap<>(); + private final Map npcUuidLookupMap = new HashMap<>(); public NpcRegistryImpl(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { this.textSerializer = textSerializer; @@ -40,49 +39,73 @@ public class NpcRegistryImpl implements NpcRegistry { } } + private void register(NpcEntryImpl entry) { + unregister(npcIdLookupMap.put(entry.getId(), entry)); + unregister(npcUuidLookupMap.put(entry.getNpc().getUuid(), entry)); + npcList.add(entry); + } + + private void unregister(NpcEntryImpl entry) { + if (entry == null) return; + npcList.remove(entry); + NpcImpl one = npcIdLookupMap.remove(entry.getId()).getNpc(); + NpcImpl two = npcUuidLookupMap.remove(entry.getNpc().getUuid()).getNpc(); + if (one != null) one.delete(); + if (two != null && !Objects.equals(one, two)) two.delete(); + } + + private void unregisterAll() { + for (NpcEntryImpl entry : npcList) entry.getNpc().delete(); + npcList.clear(); + npcIdLookupMap.clear(); + npcUuidLookupMap.clear(); + } + public void registerAll(Collection entries) { - for (NpcEntryImpl entry : entries) { - NpcEntryImpl old = npcMap.put(entry.getId(), entry); - if (old != null) old.getNpc().delete(); - } + for (NpcEntryImpl entry : entries) register(entry); } public void reload() { - for (NpcEntryImpl entry : npcMap.values()) entry.getNpc().delete(); - npcMap.clear(); - for (NpcEntryImpl entry : storage.loadNpcs()) npcMap.put(entry.getId(), entry); + unregisterAll(); + registerAll(storage.loadNpcs()); } public void save() { - storage.saveNpcs(npcMap.values().stream().filter(NpcEntryImpl::isSave).collect(Collectors.toList())); + storage.saveNpcs(npcList.stream().filter(NpcEntryImpl::isSave).collect(Collectors.toList())); } - public NpcEntryImpl get(String id) { - return npcMap.get(id.toLowerCase()); + @Override + public NpcEntryImpl getById(String id) { + return npcIdLookupMap.get(id.toLowerCase()); + } + + @Override + public NpcEntry getByUuid(UUID uuid) { + return npcUuidLookupMap.get(uuid); } public Collection getAll() { - return Collections.unmodifiableCollection(npcMap.values()); + return Collections.unmodifiableCollection(npcList); } public Collection getProcessable() { - return Collections.unmodifiableCollection(npcMap.values().stream() + return Collections.unmodifiableCollection(npcList.stream() .filter(NpcEntryImpl::isProcessed) .collect(Collectors.toList())); } public Collection getAllModifiable() { - return Collections.unmodifiableCollection(npcMap.values().stream() + return Collections.unmodifiableCollection(npcList.stream() .filter(NpcEntryImpl::isAllowCommandModification) .collect(Collectors.toList())); } public NpcEntryImpl getByEntityId(int id) { - return getAll().stream().filter(entry -> entry.getNpc().getEntity().getEntityId() == id).findFirst().orElse(null); + return npcList.stream().filter(entry -> entry.getNpc().getEntity().getEntityId() == id).findFirst().orElse(null); } public Collection getAllIds() { - return Collections.unmodifiableSet(npcMap.keySet()); + return Collections.unmodifiableSet(npcIdLookupMap.keySet()); } @Override @@ -98,7 +121,7 @@ public class NpcRegistryImpl implements NpcRegistry { } public Collection getModifiableIds() { - return Collections.unmodifiableSet(npcMap.entrySet().stream() + return Collections.unmodifiableSet(npcIdLookupMap.entrySet().stream() .filter(entry -> entry.getValue().isAllowCommandModification()) .map(Map.Entry::getKey) .collect(Collectors.toSet())); @@ -110,17 +133,17 @@ public class NpcRegistryImpl implements NpcRegistry { public NpcEntryImpl create(String id, World world, NpcTypeImpl type, NpcLocation location) { id = id.toLowerCase(); - if (npcMap.containsKey(id)) throw new IllegalArgumentException("An npc with the id " + id + " already exists!"); - NpcImpl npc = new NpcImpl(configManager, textSerializer, world, type, location, packetFactory); + if (npcIdLookupMap.containsKey(id)) throw new IllegalArgumentException("An npc with the id " + id + " already exists!"); + NpcImpl npc = new NpcImpl(UUID.randomUUID(), configManager, textSerializer, world, type, location, packetFactory); NpcEntryImpl entry = new NpcEntryImpl(id, npc); - npcMap.put(id, entry); + npcIdLookupMap.put(id, entry); return entry; } @Override public void delete(String id) { id = id.toLowerCase(); - if (!npcMap.containsKey(id)) return; - npcMap.remove(id).getNpc().delete(); + if (!npcIdLookupMap.containsKey(id)) return; + npcIdLookupMap.remove(id).getNpc().delete(); } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/parsers/NpcEntryParser.java b/plugin/src/main/java/lol/pyr/znpcsplus/parsers/NpcEntryParser.java index 5b26e4b..bb452fa 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/parsers/NpcEntryParser.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/parsers/NpcEntryParser.java @@ -19,7 +19,7 @@ public class NpcEntryParser extends ParserType { @Override public NpcEntryImpl parse(Deque deque) throws CommandExecutionException { - NpcEntryImpl entry = npcRegistry.get(deque.pop()); + NpcEntryImpl entry = npcRegistry.getById(deque.pop()); if (entry == null || !entry.isAllowCommandModification()) throw new CommandExecutionException(); return entry; } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java b/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java index f2f0e44..3b150e5 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/storage/yaml/YamlStorage.java @@ -48,7 +48,8 @@ public class YamlStorage implements NpcStorage { List npcs = new ArrayList<>(files.length); for (File file : files) if (file.isFile() && file.getName().toLowerCase().endsWith(".yml")) { YamlConfiguration config = YamlConfiguration.loadConfiguration(file); - NpcImpl npc = new NpcImpl(configManager, packetFactory, textSerializer, config.getString("world"), + UUID uuid = config.contains("uuid") ? UUID.fromString(config.getString("uuid")) : UUID.randomUUID(); + NpcImpl npc = new NpcImpl(uuid, configManager, packetFactory, textSerializer, config.getString("world"), typeRegistry.getByName(config.getString("type")), deserializeLocation(config.getConfigurationSection("location"))); ConfigurationSection properties = config.getConfigurationSection("properties"); @@ -83,6 +84,7 @@ public class YamlStorage implements NpcStorage { config.set("allow-commands", entry.isAllowCommandModification()); NpcImpl npc = entry.getNpc(); + config.set("uuid", npc.getUuid().toString()); config.set("world", npc.getWorldName()); config.set("location", serializeLocation(npc.getLocation())); config.set("type", npc.getType().getName());