/* * Copyright (c) PikaMug and contributors * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package me.pikamug.quests; import me.pikamug.localelib.LocaleManager; import me.pikamug.quests.actions.Action; import me.pikamug.quests.actions.BukkitActionFactory; import me.pikamug.quests.conditions.BukkitConditionFactory; import me.pikamug.quests.conditions.Condition; import me.pikamug.quests.config.BukkitConfigSettings; import me.pikamug.quests.config.ConfigSettings; import me.pikamug.quests.convo.misc.NpcOfferQuestPrompt; import me.pikamug.quests.dependencies.BukkitDenizenTrigger; import me.pikamug.quests.dependencies.BukkitDependencies; import me.pikamug.quests.interfaces.ReloadCallback; import me.pikamug.quests.listeners.BukkitBlockListener; import me.pikamug.quests.listeners.BukkitCitizensListener; import me.pikamug.quests.listeners.BukkitCommandManager; import me.pikamug.quests.listeners.BukkitConvoListener; import me.pikamug.quests.listeners.BukkitItemListener; import me.pikamug.quests.listeners.BukkitPartiesListener; import me.pikamug.quests.listeners.BukkitPlayerListener; import me.pikamug.quests.listeners.BukkitUniteListener; import me.pikamug.quests.listeners.BukkitZnpcsApiListener; import me.pikamug.quests.listeners.BukkitZnpcsListener; import me.pikamug.quests.logging.BukkitQuestsLog4JFilter; import me.pikamug.quests.module.CustomObjective; import me.pikamug.quests.module.CustomRequirement; import me.pikamug.quests.module.CustomReward; import me.pikamug.quests.player.BukkitQuester; import me.pikamug.quests.player.Quester; import me.pikamug.quests.quests.BukkitQuestFactory; import me.pikamug.quests.quests.Quest; import me.pikamug.quests.statistics.BukkitMetrics; import me.pikamug.quests.storage.BukkitStorageFactory; import me.pikamug.quests.storage.QuesterStorage; import me.pikamug.quests.storage.implementation.ModuleStorageImpl; import me.pikamug.quests.storage.implementation.file.BukkitActionYamlStorage; import me.pikamug.quests.storage.implementation.file.BukkitConditionYamlStorage; import me.pikamug.quests.storage.implementation.file.BukkitQuestYamlStorage; import me.pikamug.quests.storage.implementation.jar.BukkitModuleJarStorage; import me.pikamug.quests.tasks.BukkitNpcEffectThread; import me.pikamug.quests.tasks.BukkitPlayerMoveThread; import me.pikamug.quests.util.BukkitLang; import me.pikamug.quests.util.BukkitUpdateChecker; import org.apache.logging.log4j.LogManager; import org.browsit.conversations.api.Conversations; import org.browsit.conversations.bukkit.BukkitConversations; import org.browsit.conversations.bukkit.BukkitConversationsForwarder; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.command.CommandExecutor; import org.bukkit.command.TabExecutor; import org.bukkit.conversations.Conversable; import org.bukkit.conversations.ConversationFactory; import org.bukkit.entity.Player; import org.bukkit.permissions.Permission; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URISyntaxException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentSkipListSet; import java.util.logging.Level; import java.util.logging.Logger; public class BukkitQuestsPlugin extends JavaPlugin implements Quests { private boolean loading = true; private String bukkitVersion = "0"; private BukkitDependencies depends; private BukkitActionYamlStorage actionLoader; private BukkitConditionYamlStorage conditionLoader; private ConfigSettings configSettings; private ModuleStorageImpl customLoader; private BukkitQuestYamlStorage questLoader; private List customObjectives = new LinkedList<>(); private List customRequirements = new LinkedList<>(); private List customRewards = new LinkedList<>(); private Collection questers = new ConcurrentSkipListSet<>(); private Collection quests = new ConcurrentSkipListSet<>(); private Collection actions = new ConcurrentSkipListSet<>(); private Collection conditions = new ConcurrentSkipListSet<>(); private Collection questNpcUuids = new ConcurrentSkipListSet<>(); private TabExecutor cmdExecutor; private ConversationFactory conversationFactory; private ConversationFactory npcConversationFactory; private BukkitQuestFactory questFactory; private BukkitActionFactory actionFactory; private BukkitConditionFactory conditionFactory; private BukkitConvoListener convoListener; private BukkitBlockListener blockListener; private BukkitItemListener itemListener; private BukkitCitizensListener citizensListener; private BukkitZnpcsListener znpcsListener; private BukkitZnpcsApiListener znpcsPlusListener; private BukkitPlayerListener playerListener; private BukkitNpcEffectThread effectThread; private BukkitPlayerMoveThread moveThread; private BukkitUniteListener uniteListener; private BukkitPartiesListener partiesListener; private BukkitDenizenTrigger trigger; private LocaleManager localeManager; private QuesterStorage storage; @Override public void onEnable() { /*----> WARNING: ORDER OF STEPS MATTERS <----*/ BukkitConversations.init(this); new BukkitConversationsForwarder().register(this); // 1 - Trigger server to initialize Legacy Material Support try { Material.matchMaterial("STONE", true); } catch (final NoSuchMethodError ignored) { } ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addFilter(new BukkitQuestsLog4JFilter()); // 2 - Initialize variables bukkitVersion = Bukkit.getServer().getBukkitVersion().split("-")[0]; actionLoader = new BukkitActionYamlStorage(this); conditionLoader = new BukkitConditionYamlStorage(this); configSettings = new BukkitConfigSettings(this); customLoader = new BukkitModuleJarStorage(this); questLoader = new BukkitQuestYamlStorage(this); try { Class.forName("me.pikamug.quests.libs.localelib.LocaleManager"); localeManager = new LocaleManager(); } catch (final Exception ignored) { getLogger().warning("LocaleLib not present! Is this a debug environment?"); } convoListener = new BukkitConvoListener(); blockListener = new BukkitBlockListener(this); itemListener = new BukkitItemListener(this); citizensListener = new BukkitCitizensListener(this); znpcsListener = new BukkitZnpcsListener(this); znpcsPlusListener = new BukkitZnpcsApiListener(this); playerListener = new BukkitPlayerListener(this); uniteListener = new BukkitUniteListener(); partiesListener = new BukkitPartiesListener(); effectThread = new BukkitNpcEffectThread(this); moveThread = new BukkitPlayerMoveThread(this); questFactory = new BukkitQuestFactory(this); actionFactory = new BukkitActionFactory(this); conditionFactory = new BukkitConditionFactory(this); depends = new BukkitDependencies(this); trigger = new BukkitDenizenTrigger(this); // 3 - Load main config configSettings.init(); if (configSettings.getLanguage().contains("-")) { final BukkitMetrics metrics = new BukkitMetrics(this); metrics.addCustomChart(new BukkitMetrics.SimplePie("language", () -> configSettings.getLanguage())); } // 4 - Setup language files try { BukkitLang.init(this); } catch (final IOException | URISyntaxException e) { e.printStackTrace(); } // 5 - Load command executor cmdExecutor = new BukkitCommandManager(this); // 6 - Load soft-depends depends.init(); // 7 - Transfer resources from jar moveStorageResource("quests.yml"); moveStorageResource("actions.yml"); moveStorageResource("conditions.yml"); saveResourceAs("quests.yml", "storage/quests.yml", false); saveResourceAs("actions.yml", "storage/actions.yml", false); saveResourceAs("conditions.yml", "storage/conditions.yml", false); // 8 - Save config with any new options getConfig().options().copyDefaults(true); getConfig().options().header("See https://pikamug.gitbook.io/quests/setup/configuration"); saveConfig(); final BukkitStorageFactory storageFactory = new BukkitStorageFactory(this); storage = storageFactory.getInstance(); // 9 - Setup commands if (getCommand("quests") != null) { Objects.requireNonNull(getCommand("quests")).setExecutor(getTabExecutor()); Objects.requireNonNull(getCommand("quests")).setTabCompleter(getTabExecutor()); } if (getCommand("questadmin") != null) { Objects.requireNonNull(getCommand("questadmin")).setExecutor(getTabExecutor()); Objects.requireNonNull(getCommand("questadmin")).setTabCompleter(getTabExecutor()); } if (getCommand("quest") != null) { Objects.requireNonNull(getCommand("quest")).setExecutor(getTabExecutor()); Objects.requireNonNull(getCommand("quest")).setTabCompleter(getTabExecutor()); } // 10 - Build conversation factories /*this.conversationFactory = new ConversationFactory(this).withModality(false) .withPrefix(context -> ChatColor.GRAY.toString()) .withFirstPrompt(new QuestAcceptPrompt(this)).withTimeout(configSettings.getAcceptTimeout()) .thatExcludesNonPlayersWithMessage("Console may not perform this conversation!") .addConversationAbandonedListener(convoListener);*/ this.npcConversationFactory = new ConversationFactory(this).withModality(false) .withFirstPrompt(new NpcOfferQuestPrompt(this)).withTimeout(configSettings.getAcceptTimeout()) .withLocalEcho(false).addConversationAbandonedListener(convoListener); // 11 - Register listeners getServer().getPluginManager().registerEvents(getBlockListener(), this); getServer().getPluginManager().registerEvents(getItemListener(), this); depends.linkCitizens(); if (depends.getZnpcsPlus() != null) { getServer().getPluginManager().registerEvents(getZnpcsListener(), this); } depends.linkZnpcsPlusApi(); getServer().getPluginManager().registerEvents(getPlayerListener(), this); if (configSettings.getStrictPlayerMovement() > 0) { final long ticks = configSettings.getStrictPlayerMovement() * 20L; getServer().getScheduler().scheduleSyncRepeatingTask(this, getPlayerMoveThread(), ticks, ticks); } if (depends.getPartyProvider() != null) { getServer().getPluginManager().registerEvents(getUniteListener(), this); } else if (depends.getPartiesApi() != null) { getServer().getPluginManager().registerEvents(getPartiesListener(), this); } // 12 - Attempt to check for updates new BukkitUpdateChecker(this, 3711).getVersion(version -> { if (!getDescription().getVersion().split("-")[0].equalsIgnoreCase(version)) { getLogger().info(ChatColor.DARK_GREEN + BukkitLang.get("updateTo").replace("", version).replace("", ChatColor.AQUA + getDescription().getWebsite())); } }); // 13 - Delay loading of quests, actions and modules delayLoadQuestInfo(); } @Override public void onDisable() { Conversations.cleanUp(); getLogger().info("Saving Quester data..."); for (final Player p : getServer().getOnlinePlayers()) { getQuester(p.getUniqueId()).saveData(); } Bukkit.getScheduler().cancelTasks(this); getLogger().info("Closing storage..."); if (storage != null) { storage.close(); } } public boolean isProVersion() { return false; } public boolean isLoading() { return loading; } public File getPluginDataFolder() { return getDataFolder(); } public Logger getPluginLogger() { return getLogger(); } public InputStream getPluginResource(String filename) { return getResource(filename); } public String getDetectedServerSoftwareVersion() { return bukkitVersion; } public BukkitDependencies getDependencies() { return depends; } public BukkitConfigSettings getConfigSettings() { return (BukkitConfigSettings) configSettings; } @Override public List getCustomObjectives() { return customObjectives; } public Optional getCustomObjective(final String className) { for (final CustomObjective co : customObjectives) { if (co.getClass().getName().equals(className)) { return Optional.of(co); } } return Optional.empty(); } public void setCustomObjectives(List customObjectives) { this.customObjectives = customObjectives; } @Override public List getCustomRequirements() { return customRequirements; } public Optional getCustomRequirement(final String className) { for (final CustomRequirement cr : customRequirements) { if (cr.getClass().getName().equals(className)) { return Optional.of(cr); } } return Optional.empty(); } public void setCustomRequirements(List customRequirements) { this.customRequirements = customRequirements; } @Override public List getCustomRewards() { return customRewards; } public Optional getCustomReward(final String className) { for (final CustomReward cr : customRewards) { if (cr.getClass().getName().equals(className)) { return Optional.of(cr); } } return Optional.empty(); } public void setCustomRewards(List customRewards) { this.customRewards = customRewards; } /** * Get every Quest loaded in memory * * @return a collection of all Quests */ public Collection getLoadedQuests() { return quests; } /** * Set every Quest loaded in memory * */ public void setLoadedQuests(final Collection quests) { this.quests = quests; } /** * Get every Action loaded in memory * * @return a collection of all Actions */ public Collection getLoadedActions() { return actions; } /** * Set every Action loaded in memory * */ public void setLoadedActions(final Collection actions) { this.actions = actions; } /** * Get every Condition loaded in memory * * @return a collection of all Conditions */ public Collection getLoadedConditions() { return conditions; } /** * Set every Condition loaded in memory * */ public void setLoadedConditions(final Collection conditions) { this.conditions = conditions; } /** * Get Quester from player UUID * * @param id Player UUID * @return new or existing Quester */ public BukkitQuester getQuester(final @NotNull UUID id) { final ConcurrentSkipListSet set = (ConcurrentSkipListSet) questers; for (final Quester q : set) { if (q != null && q.getUUID().equals(id)) { return (BukkitQuester) q; } } final BukkitQuester quester = new BukkitQuester(this, id); if (depends.getCitizens() != null) { if (depends.getCitizens().getNPCRegistry().getByUniqueId(id) != null) { return quester; } } final BukkitQuester q = new BukkitQuester(this, id); questers.add(q); return q; } /** * Get every online Quester playing on this server * * @return a collection of all online Questers */ public Collection getOnlineQuesters() { final Collection questers = new ConcurrentSkipListSet<>(); for (final Quester q : getOfflineQuesters()) { if (q.getOfflinePlayer().isOnline()) { // Workaround for issues with the compass on fast join q.findCompassTarget(); questers.add(q); } } return questers; } /** * Get every Quester that has ever played on this server * * @return a collection of all Questers */ public Collection getOfflineQuesters() { return questers; } /** * Set every Quester that has ever played on this server * * @param questers a collection of Questers */ public void setOfflineQuesters(final Collection questers) { this.questers = new ConcurrentSkipListSet<>(questers); } /** * Get every NPC UUID which sees use a quest giver, talk target, or kill target * * @return a collection of all UUIDs */ public Collection getQuestNpcUuids() { return questNpcUuids; } /** * Set every NPC UUID which sees use a quest giver, talk target, or kill target * * @param questNpcUuids a collection of UUIDs */ @SuppressWarnings("unused") public void setQuestNpcUuids(final Collection questNpcUuids) { this.questNpcUuids = new ConcurrentSkipListSet<>(questNpcUuids); } @SuppressWarnings("unused") public CommandExecutor getCommandExecutor() { return cmdExecutor; } public TabExecutor getTabExecutor() { return cmdExecutor; } public ConversationFactory getConversationFactory() { return conversationFactory; } public ConversationFactory getNpcConversationFactory() { return npcConversationFactory; } public BukkitQuestFactory getQuestFactory() { return questFactory; } public BukkitActionFactory getActionFactory() { return actionFactory; } public BukkitConditionFactory getConditionFactory() { return conditionFactory; } public BukkitConvoListener getConvoListener() { return convoListener; } public BukkitBlockListener getBlockListener() { return blockListener; } public BukkitItemListener getItemListener() { return itemListener; } public BukkitCitizensListener getCitizensListener() { return citizensListener; } public BukkitZnpcsListener getZnpcsListener() { return znpcsListener; } public BukkitZnpcsApiListener getZNpcsPlusListener() { return znpcsPlusListener; } public BukkitPlayerListener getPlayerListener() { return playerListener; } public BukkitUniteListener getUniteListener() { return uniteListener; } public BukkitNpcEffectThread getNpcEffectThread() { return effectThread; } public BukkitPlayerMoveThread getPlayerMoveThread() { return moveThread; } public BukkitPartiesListener getPartiesListener() { return partiesListener; } public BukkitDenizenTrigger getDenizenTrigger() { return trigger; } public LocaleManager getLocaleManager() { return localeManager; } public QuesterStorage getStorage() { return storage; } /** * Move a storage file from legacy location, if present * * @param fileName Name of file to attempt move */ private void moveStorageResource(String fileName) { final File storageFile = new File(getDataFolder(), fileName); if (!storageFile.isFile()) { return; } final File outFile = new File(getDataFolder(), "storage" + File.separatorChar + fileName); final File outDir = new File(outFile.getPath().replace(fileName, "")); if (!outDir.exists()) { if (!outDir.mkdirs()) { getLogger().log(Level.SEVERE, "Failed to make directories for " + fileName + " (canWrite= " + outDir.canWrite() + ")"); } } final boolean moved = storageFile.renameTo(outFile); if (!moved) { getLogger().severe("Unable to move " + fileName + " file. Check folder permissions and restart server."); getServer().getPluginManager().disablePlugin(this); setEnabled(false); } } /** * Save a Quests plugin resource to a specific path in the filesystem * * @param resourcePath jar file location starting from resource folder, i.e. "lang/el-GR/strings.yml" * @param outputPath file destination starting from Quests folder, i.e. "lang/el-GR/strings.yml" * @param replace whether or not to replace the destination file */ @Override public void saveResourceAs(String resourcePath, final String outputPath, final boolean replace) { if (resourcePath == null || resourcePath.equals("")) { throw new IllegalArgumentException("ResourcePath cannot be null or empty"); } resourcePath = resourcePath.replace('\\', '/'); final InputStream in = getResource(resourcePath); if (in == null) { throw new IllegalArgumentException("The embedded resource '" + resourcePath + "' cannot be found in Quests jar"); } final String outPath = outputPath.replace('/', File.separatorChar).replace('\\', File.separatorChar); final File outFile = new File(getDataFolder(), outPath); final File outDir = new File(outFile.getPath().replace(outFile.getName(), "")); if (!outDir.exists()) { if (!outDir.mkdirs()) { getLogger().log(Level.SEVERE, "Failed to make directories for " + outFile.getName() + " (canWrite= " + outDir.canWrite() + ")"); } } try { if (!outFile.exists() || replace) { final OutputStream out = new FileOutputStream(outFile); final byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } out.close(); in.close(); if (!outFile.exists()) { getLogger().severe("Unable to copy " + outFile.getName() + " (canWrite= " + outFile.canWrite() + ")"); } } } catch (final IOException ex) { getLogger().log(Level.SEVERE, "Could not save " + outFile.getName() + " to " + outFile, ex); } } /** * Load quests, actions, conditions, and modules
* At startup, this lets soft-depends (namely Citizens) fully load first */ private void delayLoadQuestInfo() { getServer().getScheduler().scheduleSyncDelayedTask(this, () -> { conditionLoader.init(); actionLoader.init(); questLoader.init(); getLogger().log(Level.INFO, "Loaded " + quests.size() + " Quest(s), " + actions.size() + " Action(s), " + conditions.size() + " Condition(s) and " + BukkitLang.size() + " Phrase(s)"); for (final Player p : getServer().getOnlinePlayers()) { final Quester quester = new BukkitQuester(BukkitQuestsPlugin.this, p.getUniqueId()); if (!quester.hasData()) { quester.saveData(); } // Workaround for issues with the compass on fast join quester.findCompassTarget(); questers.add(quester); } if (depends.getCitizens() != null) { if (depends.getCitizens().getNPCRegistry() == null) { getLogger().log(Level.SEVERE, "Citizens was enabled but NPCRegistry was null. Disabling linkage."); depends.unlinkCitizens(); } } if (depends.getZnpcsPlusApi() != null) { if (depends.getZnpcsPlusApi().getNpcRegistry() == null) { getLogger().log(Level.SEVERE, "ZNPCsPlus was enabled but NpcRegistry was null. Disabling linkage."); depends.unlinkZnpcsPlusApi(); } } customLoader.init(); questLoader.importQuests(); if (getConfigSettings().canDisableCommandFeedback()) { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "gamerule sendCommandFeedback false"); } loading = false; }, 5L); } /** * Reload quests, actions, conditions, config settings, lang, modules, and player data */ public void reload(final ReloadCallback callback) { if (loading) { getLogger().warning(ChatColor.YELLOW + BukkitLang.get("errorLoading")); return; } loading = true; reloadConfig(); Bukkit.getScheduler().runTaskAsynchronously(this, () -> { try { getStorage().saveOfflineQuesters().get(); BukkitLang.clear(); configSettings.init(); BukkitLang.load(BukkitQuestsPlugin.this, configSettings.getLanguage()); quests.clear(); actions.clear(); conditions.clear(); conditionLoader.init(); actionLoader.init(); questLoader.init(); for (final Quester quester : questers) { final Quester loaded = getStorage().loadQuester(quester.getUUID()).get(); for (final Quest quest : loaded.getCurrentQuests().keySet()) { loaded.checkQuest(quest); } } customLoader.init(); questLoader.importQuests(); finishLoading(callback, true, null); } catch (final Exception e) { finishLoading(callback, false, e); } loading = false; }); } /** * Execute finishing task and print provided exception * * @param callback Callback to execute * @param result Result to pass through callback * @param exception Exception to print, or null */ private void finishLoading(final ReloadCallback callback, boolean result, final Exception exception) { if (exception != null) { exception.printStackTrace(); } if (callback != null) { Bukkit.getScheduler().runTask(BukkitQuestsPlugin.this, () -> { loading = false; callback.execute(result); }); } } /** * Checks if player can use the Quests plugin * * @param uuid the entity UUID to be checked * @return {@code true} if entity is a Player that has permission */ public boolean canUseQuests(final UUID uuid) { final Player p = Bukkit.getPlayer(uuid); if (p != null) { for (final Permission perm : getDescription().getPermissions()) { if (p.hasPermission(perm.getName())) { return true; } } } return false; } /** * Checks if conversable is non-op, non-* player in Trial Mode * * @param conversable the editor user to be checked * @return {@code true} if user is a Player with quests.mode.trial permission */ public boolean hasLimitedAccess(final Conversable conversable) { if (!(conversable instanceof Player)) { return false; } final Player player = ((Player)conversable); if (player.isOp() || player.hasPermission("*")) { return false; } return player.hasPermission("quests.mode.trial"); } /** * Get a Quest by ID * * @param id ID of the quest * @return Exact match or null if not found * @since 3.8.6 */ public Quest getQuestById(final String id) { if (id == null) { return null; } for (final Quest q : quests) { if (q.getId().equals(id)) { return q; } } return null; } /** * Get a Quest by name * * @param name Name of the quest * @return Closest match or null if not found */ public Quest getQuest(final String name) { if (name == null) { return null; } for (final Quest q : quests) { if (q.getName().equalsIgnoreCase(ChatColor.translateAlternateColorCodes('&', name))) { return q; } } for (final Quest q : quests) { if (q.getName().toLowerCase().startsWith(ChatColor.translateAlternateColorCodes('&', name).toLowerCase())) { return q; } } for (final Quest q : quests) { if (q.getName().toLowerCase().contains(ChatColor.translateAlternateColorCodes('&', name).toLowerCase())) { return q; } } for (final Quest q : quests) { // For tab completion if (ChatColor.stripColor(q.getName()).equals(ChatColor.stripColor(ChatColor .translateAlternateColorCodes('&', name)))) { return q; } } return null; } /** * Get an Action by name * * @param name Name of the action * @return Closest match or null if not found */ public Action getAction(final String name) { if (name == null) { return null; } for (final Action a : actions) { if (a.getName().equalsIgnoreCase(ChatColor.translateAlternateColorCodes('&', name))) { return a; } } for (final Action a : actions) { if (a.getName().toLowerCase().startsWith(ChatColor.translateAlternateColorCodes('&', name).toLowerCase())) { return a; } } for (final Action a : actions) { if (a.getName().toLowerCase().contains(ChatColor.translateAlternateColorCodes('&', name).toLowerCase())) { return a; } } for (final Action a : actions) { // For tab completion if (ChatColor.stripColor(a.getName()).equals(ChatColor.stripColor(ChatColor. translateAlternateColorCodes('&', name)))) { return a; } } return null; } /** * Get a Condition by name * * @param name Name of the condition * @return Closest match or null if not found */ public Condition getCondition(final String name) { if (name == null) { return null; } for (final Condition c : conditions) { if (c.getName().equalsIgnoreCase(ChatColor.translateAlternateColorCodes('&', name))) { return c; } } for (final Condition c : conditions) { if (c.getName().toLowerCase().startsWith(ChatColor.translateAlternateColorCodes('&', name).toLowerCase())) { return c; } } for (final Condition c : conditions) { if (c.getName().toLowerCase().contains(ChatColor.translateAlternateColorCodes('&', name).toLowerCase())) { return c; } } for (final Condition c : conditions) { // For tab completion if (ChatColor.stripColor(c.getName()).equals(ChatColor.stripColor(ChatColor .translateAlternateColorCodes('&', name)))) { return c; } } return null; } }