package lol.pyr.znpcsplus.npc; import lol.pyr.znpcsplus.ZNpcsPlus; import lol.pyr.znpcsplus.api.entity.EntityProperty; import lol.pyr.znpcsplus.api.interaction.InteractionAction; import lol.pyr.znpcsplus.api.npc.NpcEntry; import lol.pyr.znpcsplus.api.npc.NpcRegistry; import lol.pyr.znpcsplus.api.npc.NpcType; import lol.pyr.znpcsplus.config.ConfigManager; import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.hologram.HologramItem; import lol.pyr.znpcsplus.hologram.HologramLine; import lol.pyr.znpcsplus.hologram.HologramText; import lol.pyr.znpcsplus.interaction.ActionRegistryImpl; import lol.pyr.znpcsplus.packets.PacketFactory; import lol.pyr.znpcsplus.scheduling.TaskScheduler; import lol.pyr.znpcsplus.storage.NpcStorage; import lol.pyr.znpcsplus.storage.NpcStorageType; import lol.pyr.znpcsplus.util.NpcLocation; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Bukkit; import org.bukkit.World; import java.util.*; import java.util.stream.Collectors; public class NpcRegistryImpl implements NpcRegistry { private NpcStorage storage; private final PacketFactory packetFactory; private final ConfigManager configManager; private final LegacyComponentSerializer textSerializer; private final EntityPropertyRegistryImpl propertyRegistry; 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, ActionRegistryImpl actionRegistry, TaskScheduler scheduler, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { this.textSerializer = textSerializer; this.propertyRegistry = propertyRegistry; storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); if (storage == null) { Bukkit.getLogger().warning("Failed to initialize storage, falling back to YAML"); storage = NpcStorageType.YAML.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); } this.packetFactory = packetFactory; this.configManager = configManager; if (configManager.getConfig().autoSaveEnabled()) { long delay = configManager.getConfig().autoSaveInterval() * 20L; scheduler.runDelayedTimerAsync(this::save, delay, delay); } } 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 : getAll()) { if (entry.isSave()) entry.getNpc().delete(); } npcList.clear(); npcIdLookupMap.clear(); npcUuidLookupMap.clear(); } public void registerAll(Collection entries) { for (NpcEntryImpl entry : entries) register(entry); } public void reload() { unregisterAll(); registerAll(storage.loadNpcs()); } public void save() { storage.saveNpcs(npcList.stream().filter(NpcEntryImpl::isSave).collect(Collectors.toList())); } @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(npcList); } public Collection getProcessable() { return Collections.unmodifiableCollection(npcList.stream() .filter(NpcEntryImpl::isProcessed) .collect(Collectors.toList())); } public Collection getAllModifiable() { return Collections.unmodifiableCollection(npcList.stream() .filter(NpcEntryImpl::isAllowCommandModification) .collect(Collectors.toList())); } public NpcEntryImpl getByEntityId(int id) { return npcList.stream().filter(entry -> entry.getNpc().getEntity().getEntityId() == id || entry.getNpc().getHologram().getLines().stream().anyMatch(line -> line.getEntityId() == id)) // Also match the holograms of npcs .findFirst().orElse(null); } public Collection getAllIds() { return Collections.unmodifiableSet(npcIdLookupMap.keySet()); } @Override public Collection getAllPlayerMade() { return getAllModifiable(); } @Override public Collection getAllPlayerMadeIds() { return getAllModifiable().stream() .map(NpcEntryImpl::getId) .collect(Collectors.toSet()); } public Collection getModifiableIds() { return Collections.unmodifiableSet(npcIdLookupMap.entrySet().stream() .filter(entry -> entry.getValue().isAllowCommandModification()) .map(Map.Entry::getKey) .collect(Collectors.toSet())); } public NpcEntryImpl create(String id, World world, NpcType type, NpcLocation location) { return create(id, world, (NpcTypeImpl) type, location); } public NpcEntryImpl create(String id, World world, NpcTypeImpl type, NpcLocation location) { id = id.toLowerCase(); if (npcIdLookupMap.containsKey(id)) throw new IllegalArgumentException("An npc with the id " + id + " already exists!"); NpcImpl npc = new NpcImpl(UUID.randomUUID(), propertyRegistry, configManager, textSerializer, world, type, location, packetFactory); type.applyDefaultProperties(npc); NpcEntryImpl entry = new NpcEntryImpl(id, npc); register(entry); return entry; } public NpcEntryImpl clone(String id, String newId, World newWorld, NpcLocation newLocation) { NpcEntryImpl oldNpc = getById(id); if (oldNpc == null) return null; NpcEntryImpl newNpc = create(newId, newWorld, oldNpc.getNpc().getType(), newLocation); newNpc.enableEverything(); for (EntityProperty property : oldNpc.getNpc().getAllProperties()) { newNpc.getNpc().UNSAFE_setProperty(property, oldNpc.getNpc().getProperty(property)); } for (InteractionAction action : oldNpc.getNpc().getActions()) { newNpc.getNpc().addAction(action); } for (HologramLine line : oldNpc.getNpc().getHologram().getLines()) { if (line instanceof HologramText) { HologramText text = (HologramText) line; newNpc.getNpc().getHologram().addTextLineComponent(text.getValue()); } else if (line instanceof HologramItem) { HologramItem item = (HologramItem) line; newNpc.getNpc().getHologram().addItemLinePEStack(item.getValue()); } else throw new IllegalArgumentException("Unknown hologram line type during clone"); } return newNpc; } @Override public void delete(String id) { NpcEntryImpl entry = npcIdLookupMap.get(id.toLowerCase()); if (entry == null) return; unregister(entry); storage.deleteNpc(entry); } public void switchIds(String oldId, String newId) { NpcEntryImpl entry = getById(oldId); delete(oldId); NpcEntryImpl newEntry = new NpcEntryImpl(newId, entry.getNpc()); newEntry.setSave(entry.isSave()); newEntry.setProcessed(entry.isProcessed()); newEntry.setAllowCommandModification(entry.isAllowCommandModification()); register(newEntry); } public void unload() { npcList.forEach(npcEntry -> npcEntry.getNpc().delete()); storage.close(); } public NpcStorage getStorage() { return storage; } }