/******************************************************************************* * This file is part of ASkyBlock. * * ASkyBlock is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ASkyBlock is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ASkyBlock. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package com.wasteofplastic.askyblock.util; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.BlockFace; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import com.wasteofplastic.askyblock.ASkyBlock; import com.wasteofplastic.askyblock.Settings; import com.wasteofplastic.askyblock.nms.NMSAbstraction; /** * A set of utility methods * * @author tastybento * */ public class Util { private static ASkyBlock plugin = ASkyBlock.getPlugin(); private static Long x = System.nanoTime(); /** * Loads a YAML file and if it does not exist it is looked for in the JAR * * @param file * @return */ public static YamlConfiguration loadYamlFile(String file) { File dataFolder = plugin.getDataFolder(); File yamlFile = new File(dataFolder, file); YamlConfiguration config = null; if (yamlFile.exists()) { try { config = new YamlConfiguration(); config.load(yamlFile); } catch (Exception e) { e.printStackTrace(); } } else { // Create the missing file config = new YamlConfiguration(); if (!file.startsWith("players")) { plugin.getLogger().info("No " + file + " found. Creating it..."); } try { if (plugin.getResource(file) != null) { plugin.getLogger().info("Using default found in jar file."); plugin.saveResource(file, false); config = new YamlConfiguration(); config.load(yamlFile); } else { config.save(yamlFile); } } catch (Exception e) { plugin.getLogger().severe("Could not create the " + file + " file!"); } } return config; } /** * Saves a YAML file * * @param yamlFile * @param fileLocation */ public static void saveYamlFile(YamlConfiguration yamlFile, String fileLocation) { File dataFolder = plugin.getDataFolder(); File file = new File(dataFolder, fileLocation); try { yamlFile.save(file); } catch (Exception e) { e.printStackTrace(); } } /** * Cuts up a string into multiple lines with the same color code at the * start of each line * * @param color * @param longLine * @param length * @return List containing the colored lines */ public static List<String> chop(ChatColor color, String longLine, int length) { List<String> result = new ArrayList<String>(); // int multiples = longLine.length() / length; int i = 0; for (i = 0; i < longLine.length(); i += length) { // for (int i = 0; i< (multiples*length); i += length) { int endIndex = Math.min(i + length, longLine.length()); String line = longLine.substring(i, endIndex); // Do the following only if i+length is not the end of the string if (endIndex < longLine.length()) { // Check if last character in this string is not a space if (!line.substring(line.length() - 1).equals(" ")) { // If it is not a space, check to see if the next character // in long line is a space. if (!longLine.substring(endIndex, endIndex + 1).equals(" ")) { // If it is not, then we are cutting a word in two and // need to backtrack to the last space if possible int lastSpace = line.lastIndexOf(" "); // Only do this if there is a space in the line to // backtrack to... if (lastSpace != -1 && lastSpace < line.length()) { line = line.substring(0, lastSpace); i -= (length - lastSpace - 1); } } } } // } result.add(color + line); } // result.add(color + longLine.substring(i, longLine.length())); return result; } /** * Converts block face direction to radial degrees. Returns 0 if block face * is not radial. * * @param face * @return degrees */ public static float blockFaceToFloat(BlockFace face) { switch (face) { case EAST: return 90F; case EAST_NORTH_EAST: return 67.5F; case EAST_SOUTH_EAST: return 0F; case NORTH: return 0F; case NORTH_EAST: return 45F; case NORTH_NORTH_EAST: return 22.5F; case NORTH_NORTH_WEST: return 337.5F; case NORTH_WEST: return 315F; case SOUTH: return 180F; case SOUTH_EAST: return 135F; case SOUTH_SOUTH_EAST: return 157.5F; case SOUTH_SOUTH_WEST: return 202.5F; case SOUTH_WEST: return 225F; case WEST: return 270F; case WEST_NORTH_WEST: return 292.5F; case WEST_SOUTH_WEST: return 247.5F; default: return 0F; } } /** * Converts a name like IRON_INGOT into Iron Ingot to improve readability * * @param ugly * The string such as IRON_INGOT * @return A nicer version, such as Iron Ingot * * Credits to mikenon on GitHub! */ public static String prettifyText(String ugly) { if (!ugly.contains("_") && (!ugly.equals(ugly.toUpperCase()))) return ugly; String fin = ""; ugly = ugly.toLowerCase(); if (ugly.contains("_")) { String[] splt = ugly.split("_"); int i = 0; for (String s : splt) { i += 1; fin += Character.toUpperCase(s.charAt(0)) + s.substring(1); if (i < splt.length) fin += " "; } } else { fin += Character.toUpperCase(ugly.charAt(0)) + ugly.substring(1); } return fin; } /** * Converts a serialized location to a Location. Returns null if string is * empty * * @param s * - serialized location in format "world:x:y:z" * @return Location */ static public Location getLocationString(final String s) { if (s == null || s.trim() == "") { return null; } final String[] parts = s.split(":"); if (parts.length == 4) { final World w = Bukkit.getServer().getWorld(parts[0]); if (w == null) { return null; } final int x = Integer.parseInt(parts[1]); final int y = Integer.parseInt(parts[2]); final int z = Integer.parseInt(parts[3]); return new Location(w, x, y, z); } else if (parts.length == 6) { final World w = Bukkit.getServer().getWorld(parts[0]); if (w == null) { return null; } final int x = Integer.parseInt(parts[1]); final int y = Integer.parseInt(parts[2]); final int z = Integer.parseInt(parts[3]); final float yaw = Float.intBitsToFloat(Integer.parseInt(parts[4])); final float pitch = Float.intBitsToFloat(Integer.parseInt(parts[5])); return new Location(w, x, y, z, yaw, pitch); } return null; } /** * Converts a location to a simple string representation * If location is null, returns empty string * * @param location * @return String of location */ static public String getStringLocation(final Location location) { if (location == null || location.getWorld() == null) { return ""; } return location.getWorld().getName() + ":" + location.getBlockX() + ":" + location.getBlockY() + ":" + location.getBlockZ() + ":" + Float.floatToIntBits(location.getYaw()) + ":" + Float.floatToIntBits(location.getPitch()); } /** * Returns all of the items that begin with the given start, * ignoring case. Intended for tabcompletion. * * @param list * @param start * @return List of items that start with the letters */ public static List<String> tabLimit(final List<String> list, final String start) { final List<String> returned = new ArrayList<String>(); for (String s : list) { if (s == null) continue; if (s.toLowerCase().startsWith(start.toLowerCase())) { returned.add(s); } } return returned; } /** * Gets a list of all players who are currently online. * * @return list of online players */ public static List<String> getOnlinePlayerList() { final List<String> returned = new ArrayList<String>(); for (Player p : Bukkit.getServer().getOnlinePlayers()) { returned.add(p.getName()); } return returned; } /** * Checks what version the server is running and picks the appropriate NMS handler, or fallback * @return NMSAbstraction class * @throws ClassNotFoundException * @throws IllegalArgumentException * @throws SecurityException * @throws InstantiationException * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException */ public static NMSAbstraction checkVersion() throws ClassNotFoundException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { String serverPackageName = Bukkit.getServer().getClass().getPackage().getName(); String pluginPackageName = ASkyBlock.getPlugin().getClass().getPackage().getName(); String version = serverPackageName.substring(serverPackageName.lastIndexOf('.') + 1); Class<?> clazz; try { //plugin.getLogger().info("Trying " + pluginPackageName + ".nms." + version + ".NMSHandler"); clazz = Class.forName(pluginPackageName + ".nms." + version + ".NMSHandler"); } catch (Exception e) { Bukkit.getLogger().info("No NMS Handler found for " + version + ", falling back to Bukkit API."); clazz = Class.forName(pluginPackageName + ".nms.fallback.NMSHandler"); } //plugin.getLogger().info("DEBUG: " + serverPackageName); //plugin.getLogger().info("DEBUG: " + pluginPackageName); // Check if we have a NMSAbstraction implementing class at that location. if (NMSAbstraction.class.isAssignableFrom(clazz)) { return (NMSAbstraction) clazz.getConstructor().newInstance(); } else { throw new IllegalStateException("Class " + clazz.getName() + " does not implement NMSAbstraction"); } } /** * Send a message to sender if message is not empty. Does not include color codes or spaces * @param sender * @param message */ public static void sendMessage(CommandSender sender, String message) { if (!ChatColor.stripColor(message).trim().isEmpty()) { sender.sendMessage(message); } } /** * @return random long number using XORShift random number generator */ public static long randomLong() { x ^= (x << 21); x ^= (x >>> 35); x ^= (x << 4); return Math.abs(x); } /** * @return random double using XORShift random number generator */ public static double randomDouble() { return (double)randomLong()/Long.MAX_VALUE; } /** * Changes the setting in config.yml to a new value without removing comments (saveConfig() removes comments) * @param oldSetting * @param newSetting * @throws IOException */ public static void setConfig(String setting, String oldSetting, String newSetting) throws IOException { setYamlConfig(plugin.getDataFolder().getAbsolutePath() + File.separator + "config.yml", setting, oldSetting, newSetting); } /** * Changes the setting in a YAML file to a new value without removing comments (saveConfig() removes comments) * @param absoluteFilename * @param setting * @param oldSetting * @param newSetting * @throws IOException */ public static void setYamlConfig(String absoluteFilename, String setting, String oldSetting, String newSetting) throws IOException { Path path = Paths.get(absoluteFilename); Charset charset = StandardCharsets.UTF_8; String content = new String(Files.readAllBytes(path), charset); content = content.replaceAll(setting + ": " + oldSetting, setting + ": " + newSetting); Files.write(path, content.getBytes(charset)); } /** * Changes a setting in all player files in the player folder. If the setting does not exist, no change is made * This is not a true YAML change, if the setting name exists multiple times in the file, all lines will be changed. * The setting must include any spaces at the front if required * @param playerFolder * @param setting - name of the YAML setting, e.g., locale * @param newSettingValue - the new value for this setting * @throws IOException */ public static void setPlayerYamlConfig(File playerFolder, String setting, String newSettingValue) throws IOException { FilenameFilter ymlFilter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { String lowercaseName = name.toLowerCase(); if (lowercaseName.endsWith(".yml")) { return true; } else { return false; } } }; for (File file: playerFolder.listFiles(ymlFilter)) { Path path = Paths.get(file.getAbsolutePath()); Charset charset = StandardCharsets.UTF_8; List<String> fileContent = new ArrayList<>(Files.readAllLines(path, charset)); for (int i = 0; i < fileContent.size(); i++) { if (fileContent.get(i).startsWith(setting)) { fileContent.set(i, setting + ": " + newSettingValue); break; } } Files.write(path, fileContent, charset); } } /** * Display message to player in action bar (1.11+ or chat) * @param player * @param message */ public static void sendEnterExit(Player player, String message) { if (!Settings.showInActionBar || plugin.getServer().getVersion().contains("(MC: 1.7") || plugin.getServer().getVersion().contains("(MC: 1.8") || plugin.getServer().getVersion().contains("(MC: 1.9") || plugin.getServer().getVersion().contains("(MC: 1.10")) { sendMessage(player, message); return; } plugin.getServer().dispatchCommand(plugin.getServer().getConsoleSender(), "minecraft:title " + player.getName() + " actionbar {\"text\":\"" + ChatColor.stripColor(message) + "\"}"); } }