849 lines
28 KiB
Java
849 lines
28 KiB
Java
package me.wiefferink.areashop.tools;
|
|
|
|
import com.sk89q.worldedit.BlockVector;
|
|
import com.sk89q.worldguard.protection.managers.RegionManager;
|
|
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
|
import me.wiefferink.areashop.AreaShop;
|
|
import me.wiefferink.areashop.interfaces.WorldEditSelection;
|
|
import me.wiefferink.areashop.regions.BuyRegion;
|
|
import me.wiefferink.areashop.regions.GeneralRegion;
|
|
import me.wiefferink.areashop.regions.RentRegion;
|
|
import me.wiefferink.interactivemessenger.Log;
|
|
import me.wiefferink.interactivemessenger.processing.Message;
|
|
import org.apache.commons.lang.exception.ExceptionUtils;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.ChatColor;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Server;
|
|
import org.bukkit.World;
|
|
import org.bukkit.block.BlockFace;
|
|
import org.bukkit.configuration.Configuration;
|
|
import org.bukkit.configuration.ConfigurationSection;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import javax.script.ScriptEngine;
|
|
import javax.script.ScriptEngineManager;
|
|
import javax.script.ScriptException;
|
|
import java.lang.reflect.Method;
|
|
import java.math.BigDecimal;
|
|
import java.math.RoundingMode;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Calendar;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
|
|
public class Utils {
|
|
|
|
// Not used
|
|
private Utils() {
|
|
}
|
|
|
|
private static YamlConfiguration config;
|
|
private static Set<String> identifiers;
|
|
private static Set<String> seconds;
|
|
private static Set<String> minutes;
|
|
private static Set<String> hours;
|
|
private static Set<String> days;
|
|
private static Set<String> weeks;
|
|
private static Set<String> months;
|
|
private static Set<String> years;
|
|
private static ScriptEngine scriptEngine;
|
|
private static Map<Double, String> suffixes;
|
|
|
|
/**
|
|
* Initialize the utilities class with constants.
|
|
* @param pluginConfig The config of the plugin
|
|
*/
|
|
public static void initialize(YamlConfiguration pluginConfig) {
|
|
config = pluginConfig;
|
|
|
|
// Setup individual identifiers
|
|
seconds = getSetAndDefaults("seconds");
|
|
minutes = getSetAndDefaults("minutes");
|
|
hours = getSetAndDefaults("hours");
|
|
days = getSetAndDefaults("days");
|
|
weeks = getSetAndDefaults("weeks");
|
|
months = getSetAndDefaults("months");
|
|
years = getSetAndDefaults("years");
|
|
// Setup all time identifiers
|
|
identifiers = new HashSet<>();
|
|
identifiers.addAll(seconds);
|
|
identifiers.addAll(minutes);
|
|
identifiers.addAll(hours);
|
|
identifiers.addAll(days);
|
|
identifiers.addAll(weeks);
|
|
identifiers.addAll(months);
|
|
identifiers.addAll(years);
|
|
|
|
suffixes = new HashMap<>();
|
|
// This stuff should not be necessary, but it is, getConfigurationSection() does not do proper fallback to defaults!
|
|
// TODO: Create a custom configuration that fixes this behavior
|
|
ConfigurationSection suffixesSection = null;
|
|
if(config.isSet("metricSymbols")) {
|
|
suffixesSection = config.getConfigurationSection("metricSymbols");
|
|
} else {
|
|
Configuration defaults = config.getDefaults();
|
|
if(defaults != null) {
|
|
suffixesSection = defaults.getConfigurationSection("metricSymbols");
|
|
}
|
|
}
|
|
if(suffixesSection != null) {
|
|
for(String key : suffixesSection.getKeys(false)) {
|
|
try {
|
|
suffixes.put(Double.parseDouble(key), suffixesSection.getString(key));
|
|
} catch(NumberFormatException e) {
|
|
Log.warn("Key '" + key + "' in the metricSymbols section of config.yml is not a number!");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a string list from the config, combined with the entries specified in the default config.
|
|
* @param path The path to read the lists from
|
|
* @return List with all values defined in the config and the default config combined
|
|
*/
|
|
private static Set<String> getSetAndDefaults(String path) {
|
|
Set<String> result = new HashSet<>(config.getStringList(path));
|
|
ConfigurationSection defaults = config.getDefaults();
|
|
if(defaults != null) {
|
|
result.addAll(defaults.getStringList(path));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Create a message with a list of parts.
|
|
* @param replacements The parts to use
|
|
* @param messagePart The message to use for the parts
|
|
* @return A Message object containing the parts combined into one message
|
|
*/
|
|
public static Message combinedMessage(Collection<?> replacements, String messagePart) {
|
|
return combinedMessage(replacements, messagePart, ", ");
|
|
}
|
|
|
|
/**
|
|
* Create a message with a list of parts.
|
|
* @param replacements The parts to use
|
|
* @param messagePart The message to use for the parts
|
|
* @param combiner The string to use as combiner
|
|
* @return A Message object containing the parts combined into one message
|
|
*/
|
|
public static Message combinedMessage(Collection<?> replacements, String messagePart, String combiner) {
|
|
Message result = Message.empty();
|
|
boolean first = true;
|
|
for(Object part : replacements) {
|
|
if(first) {
|
|
first = false;
|
|
} else {
|
|
result.append(combiner);
|
|
}
|
|
result.append(Message.fromKey(messagePart).replacements(part));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets the online players.
|
|
* Provides backwards compatibility for 1.7- where it returns an array
|
|
* @return Online players
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public static Collection<? extends Player> getOnlinePlayers() {
|
|
try {
|
|
Method onlinePlayerMethod = Server.class.getMethod("getOnlinePlayers");
|
|
if(onlinePlayerMethod.getReturnType().equals(Collection.class)) {
|
|
return ((Collection<? extends Player>)onlinePlayerMethod.invoke(Bukkit.getServer()));
|
|
} else {
|
|
return Arrays.asList((Player[])onlinePlayerMethod.invoke(Bukkit.getServer()));
|
|
}
|
|
} catch(Exception ex) {
|
|
AreaShop.debug("getOnlinePlayers error: " + ex.getMessage());
|
|
}
|
|
return new HashSet<>();
|
|
}
|
|
|
|
/**
|
|
* Create a map from a location, to save it in the config.
|
|
* @param location The location to transform
|
|
* @param setPitchYaw true to save the pitch and yaw, otherwise false
|
|
* @return The map with the location values
|
|
*/
|
|
public static ConfigurationSection locationToConfig(Location location, boolean setPitchYaw) {
|
|
if(location == null) {
|
|
return null;
|
|
}
|
|
ConfigurationSection result = new YamlConfiguration();
|
|
result.set("world", location.getWorld().getName());
|
|
result.set("x", location.getX());
|
|
result.set("y", location.getY());
|
|
result.set("z", location.getZ());
|
|
if(setPitchYaw) {
|
|
result.set("yaw", Float.toString(location.getYaw()));
|
|
result.set("pitch", Float.toString(location.getPitch()));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Create a map from a location, to save it in the config (without pitch and yaw).
|
|
* @param location The location to transform
|
|
* @return The map with the location values
|
|
*/
|
|
public static ConfigurationSection locationToConfig(Location location) {
|
|
return locationToConfig(location, false);
|
|
}
|
|
|
|
/**
|
|
* Create a location from a map, reconstruction from the config values.
|
|
* @param config The config section to reconstruct from
|
|
* @return The location
|
|
*/
|
|
public static Location configToLocation(ConfigurationSection config) {
|
|
if(config == null
|
|
|| !config.isString("world")
|
|
|| !config.isDouble("x")
|
|
|| !config.isDouble("y")
|
|
|| !config.isDouble("z")
|
|
|| Bukkit.getWorld(config.getString("world")) == null) {
|
|
return null;
|
|
}
|
|
Location result = new Location(
|
|
Bukkit.getWorld(config.getString("world")),
|
|
config.getDouble("x"),
|
|
config.getDouble("y"),
|
|
config.getDouble("z"));
|
|
if(config.isString("yaw") && config.isString("pitch")) {
|
|
result.setPitch(Float.parseFloat(config.getString("pitch")));
|
|
result.setYaw(Float.parseFloat(config.getString("yaw")));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Create a comma-separated list.
|
|
* @param input Collection of object which should be concatenated with comma's in between (skipping null values)
|
|
* @return Innput object concatenated with comma's in between
|
|
*/
|
|
public static String createCommaSeparatedList(Collection<?> input) {
|
|
StringBuilder result = new StringBuilder();
|
|
boolean first = true;
|
|
for(Object object : input) {
|
|
if(object != null) {
|
|
if(first) {
|
|
first = false;
|
|
result.append(object.toString());
|
|
} else {
|
|
result.append(", ").append(object.toString());
|
|
}
|
|
}
|
|
}
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* Convert milliseconds to ticks.
|
|
* @param milliseconds Milliseconds to convert
|
|
* @return milliseconds divided by 50 (20 ticks per second)
|
|
*/
|
|
public static long millisToTicks(long milliseconds) {
|
|
return milliseconds / 50;
|
|
}
|
|
|
|
/**
|
|
* Convert milliseconds to a human readable format.
|
|
* @param milliseconds The amount of milliseconds to convert
|
|
* @return A formatted string based on the language file
|
|
*/
|
|
public static String millisToHumanFormat(long milliseconds) {
|
|
long timeLeft = milliseconds + 500;
|
|
// To seconds
|
|
timeLeft = timeLeft / 1000;
|
|
if(timeLeft <= 0) {
|
|
return Message.fromKey("timeleft-ended").getPlain();
|
|
} else if(timeLeft == 1) {
|
|
return Message.fromKey("timeleft-second").replacements(timeLeft).getPlain();
|
|
} else if(timeLeft <= 120) {
|
|
return Message.fromKey("timeleft-seconds").replacements(timeLeft).getPlain();
|
|
}
|
|
// To minutes
|
|
timeLeft = timeLeft / 60;
|
|
if(timeLeft <= 120) {
|
|
return Message.fromKey("timeleft-minutes").replacements(timeLeft).getPlain();
|
|
}
|
|
// To hours
|
|
timeLeft = timeLeft / 60;
|
|
if(timeLeft <= 48) {
|
|
return Message.fromKey("timeleft-hours").replacements(timeLeft).getPlain();
|
|
}
|
|
// To days
|
|
timeLeft = timeLeft / 24;
|
|
if(timeLeft <= 60) {
|
|
return Message.fromKey("timeleft-days").replacements(timeLeft).getPlain();
|
|
}
|
|
// To months
|
|
timeLeft = timeLeft / 30;
|
|
if(timeLeft <= 24) {
|
|
return Message.fromKey("timeleft-months").replacements(timeLeft).getPlain();
|
|
}
|
|
// To years
|
|
timeLeft = timeLeft / 12;
|
|
return Message.fromKey("timeleft-years").replacements(timeLeft).getPlain();
|
|
}
|
|
|
|
private static final BlockFace[] facings = {BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST, BlockFace.WEST, BlockFace.NORTH_WEST};
|
|
|
|
/**
|
|
* Get the facing direction based on the yaw.
|
|
* @param yaw The horizontal angle that for example the player is looking
|
|
* @return The Block Face of the angle
|
|
*/
|
|
public static BlockFace yawToFacing(float yaw) {
|
|
return facings[Math.round(yaw / 45f) & 0x7];
|
|
}
|
|
|
|
// ======================================================================
|
|
// Methods to get WorldGuard or AreaShop regions by location or selection
|
|
// ======================================================================
|
|
|
|
/**
|
|
* Get all AreaShop regions intersecting with a WorldEdit selection.
|
|
* @param selection The selection to check
|
|
* @return A list with all the AreaShop regions intersecting with the selection
|
|
*/
|
|
public static List<GeneralRegion> getRegionsInSelection(WorldEditSelection selection) {
|
|
ArrayList<GeneralRegion> result = new ArrayList<>();
|
|
for(ProtectedRegion region : getWorldEditRegionsInSelection(selection)) {
|
|
GeneralRegion asRegion = AreaShop.getInstance().getFileManager().getRegion(region.getId());
|
|
if(asRegion != null) {
|
|
result.add(asRegion);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get all AreaShop regions containing a location.
|
|
* @param location The location to check
|
|
* @return A list with all the AreaShop regions that contain the location
|
|
*/
|
|
public static List<GeneralRegion> getRegions(Location location) {
|
|
return getRegionsInSelection(new WorldEditSelection(location.getWorld(), location, location));
|
|
}
|
|
|
|
/**
|
|
* Get all WorldGuard regions intersecting with a WorldEdit selection.
|
|
* @param selection The selection to check
|
|
* @return A list with all the WorldGuard regions intersecting with the selection
|
|
*/
|
|
public static List<ProtectedRegion> getWorldEditRegionsInSelection(WorldEditSelection selection) {
|
|
// Get all regions inside or intersecting with the WorldEdit selection of the player
|
|
World world = selection.getWorld();
|
|
RegionManager regionManager = AreaShop.getInstance().getRegionManager(world);
|
|
ArrayList<ProtectedRegion> result = new ArrayList<>();
|
|
Location selectionMin = selection.getMinimumLocation();
|
|
Location selectionMax = selection.getMaximumLocation();
|
|
for(ProtectedRegion region : regionManager.getRegions().values()) {
|
|
BlockVector regionMin = region.getMinimumPoint();
|
|
BlockVector regionMax = region.getMaximumPoint();
|
|
if(
|
|
( // x part, resolves to true if the selection and region overlap anywhere on the x-axis
|
|
(regionMin.getBlockX() <= selectionMax.getBlockX() && regionMin.getBlockX() >= selectionMin.getBlockX())
|
|
|| (regionMax.getBlockX() <= selectionMax.getBlockX() && regionMax.getBlockX() >= selectionMin.getBlockX())
|
|
|| (selectionMin.getBlockX() >= regionMin.getBlockX() && selectionMin.getBlockX() <= regionMax.getBlockX())
|
|
|| (selectionMax.getBlockX() >= regionMin.getBlockX() && selectionMax.getBlockX() <= regionMax.getBlockX())
|
|
) && ( // Y part, resolves to true if the selection and region overlap anywhere on the y-axis
|
|
(regionMin.getBlockY() <= selectionMax.getBlockY() && regionMin.getBlockY() >= selectionMin.getBlockY())
|
|
|| (regionMax.getBlockY() <= selectionMax.getBlockY() && regionMax.getBlockY() >= selectionMin.getBlockY())
|
|
|| (selectionMin.getBlockY() >= regionMin.getBlockY() && selectionMin.getBlockY() <= regionMax.getBlockY())
|
|
|| (selectionMax.getBlockY() >= regionMin.getBlockY() && selectionMax.getBlockY() <= regionMax.getBlockY())
|
|
) && ( // Z part, resolves to true if the selection and region overlap anywhere on the z-axis
|
|
(regionMin.getBlockZ() <= selectionMax.getBlockZ() && regionMin.getBlockZ() >= selectionMin.getBlockZ())
|
|
|| (regionMax.getBlockZ() <= selectionMax.getBlockZ() && regionMax.getBlockZ() >= selectionMin.getBlockZ())
|
|
|| (selectionMin.getBlockZ() >= regionMin.getBlockZ() && selectionMin.getBlockZ() <= regionMax.getBlockZ())
|
|
|| (selectionMax.getBlockZ() >= regionMin.getBlockZ() && selectionMax.getBlockZ() <= regionMax.getBlockZ())
|
|
)
|
|
) {
|
|
result.add(region);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get a list of regions around a location.
|
|
* - Returns highest priority, child instead of parent regions
|
|
* @param location The location to check for regions
|
|
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
|
|
*/
|
|
public static List<ProtectedRegion> getImportantWorldEditRegions(Location location) {
|
|
List<ProtectedRegion> result = new ArrayList<>();
|
|
Set<ProtectedRegion> regions = AreaShop.getInstance().getWorldGuardHandler().getApplicableRegionsSet(location);
|
|
if(regions != null) {
|
|
boolean first = true;
|
|
for(ProtectedRegion pr : regions) {
|
|
if(first) {
|
|
result.add(pr);
|
|
first = false;
|
|
} else {
|
|
if(pr.getPriority() > result.get(0).getPriority()) {
|
|
result.clear();
|
|
result.add(pr);
|
|
} else if(pr.getParent() != null && pr.getParent().equals(result.get(0))) {
|
|
result.clear();
|
|
result.add(pr);
|
|
} else {
|
|
result.add(pr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get the most important rental AreaShop regions.
|
|
* - Returns highest priority, child instead of parent regions.
|
|
* @param location The location to check for regions
|
|
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
|
|
*/
|
|
public static List<RentRegion> getImportantRentRegions(Location location) {
|
|
List<RentRegion> result = new ArrayList<>();
|
|
for(GeneralRegion region : getImportantRegions(location, GeneralRegion.RegionType.RENT)) {
|
|
result.add((RentRegion)region);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get the most important buy AreaShop regions.
|
|
* - Returns highest priority, child instead of parent regions.
|
|
* @param location The location to check for regions
|
|
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
|
|
*/
|
|
public static List<BuyRegion> getImportantBuyRegions(Location location) {
|
|
List<BuyRegion> result = new ArrayList<>();
|
|
for(GeneralRegion region : getImportantRegions(location, GeneralRegion.RegionType.BUY)) {
|
|
result.add((BuyRegion)region);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get the most important AreaShop regions.
|
|
* - Returns highest priority, child instead of parent regions.
|
|
* @param location The location to check for regions
|
|
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
|
|
*/
|
|
public static List<GeneralRegion> getImportantRegions(Location location) {
|
|
return getImportantRegions(location, null);
|
|
}
|
|
|
|
/**
|
|
* Get the most important AreaShop regions.
|
|
* - Returns highest priority, child instead of parent regions.
|
|
* @param location The location to check for regions
|
|
* @param type The type of regions to look for, null for all
|
|
* @return empty list if no regions found, 1 member if 1 region is a priority, more if regions with the same priority
|
|
*/
|
|
public static List<GeneralRegion> getImportantRegions(Location location, GeneralRegion.RegionType type) {
|
|
List<GeneralRegion> result = new ArrayList<>();
|
|
Set<ProtectedRegion> regions = AreaShop.getInstance().getWorldGuardHandler().getApplicableRegionsSet(location);
|
|
if(regions != null) {
|
|
List<GeneralRegion> candidates = new ArrayList<>();
|
|
for(ProtectedRegion pr : regions) {
|
|
GeneralRegion region = AreaShop.getInstance().getFileManager().getRegion(pr.getId());
|
|
if(region != null && (
|
|
(type == GeneralRegion.RegionType.RENT && region instanceof RentRegion)
|
|
|| (type == GeneralRegion.RegionType.BUY && region instanceof BuyRegion)
|
|
|| type == null)) {
|
|
candidates.add(region);
|
|
}
|
|
}
|
|
boolean first = true;
|
|
for(GeneralRegion region : candidates) {
|
|
if(region == null) {
|
|
AreaShop.debug("skipped null region");
|
|
continue;
|
|
}
|
|
if(first) {
|
|
result.add(region);
|
|
first = false;
|
|
} else {
|
|
if(region.getRegion().getPriority() > result.get(0).getRegion().getPriority()) {
|
|
result.clear();
|
|
result.add(region);
|
|
} else if(region.getRegion().getParent() != null && region.getRegion().getParent().equals(result.get(0).getRegion())) {
|
|
result.clear();
|
|
result.add(region);
|
|
} else {
|
|
result.add(region);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return new ArrayList<>(result);
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert color and formatting codes to bukkit values.
|
|
* @param input Start string with color and formatting codes in it
|
|
* @return String with the color and formatting codes in the bukkit format
|
|
*/
|
|
public static String applyColors(String input) {
|
|
String result = null;
|
|
if(input != null) {
|
|
result = ChatColor.translateAlternateColorCodes('&', input);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* Format the currency amount with the characters before and after.
|
|
* @param amount Amount of money to format
|
|
* @return Currency character format string
|
|
*/
|
|
public static String formatCurrency(double amount) {
|
|
String before = config.getString("moneyCharacter");
|
|
before = before.replace(AreaShop.currencyEuro, "€");
|
|
String after = config.getString("moneyCharacterAfter");
|
|
after = after.replace(AreaShop.currencyEuro, "€");
|
|
String result;
|
|
// Check for infinite and NaN
|
|
if(Double.isInfinite(amount)) {
|
|
result = "\u221E"; // Infinite symbol
|
|
} else if(Double.isNaN(amount)) {
|
|
result = "NaN";
|
|
} else {
|
|
BigDecimal bigDecimal = BigDecimal.valueOf(amount);
|
|
boolean stripTrailingZeros = false;
|
|
int fractionalNumber = config.getInt("fractionalNumbers");
|
|
// Add metric suffix if necessary
|
|
if(config.getDouble("metricSuffixesAbove") != -1) {
|
|
String suffix = null;
|
|
double divider = 1;
|
|
for(Double number : suffixes.keySet()) {
|
|
if(amount >= number && number > divider) {
|
|
divider = number;
|
|
suffix = suffixes.get(number);
|
|
}
|
|
}
|
|
if(suffix != null) {
|
|
bigDecimal = BigDecimal.valueOf(amount / divider);
|
|
after = suffix + after;
|
|
fractionalNumber = config.getInt("fractionalNumbersShort");
|
|
stripTrailingZeros = true;
|
|
}
|
|
}
|
|
|
|
// Round if necessary
|
|
if(fractionalNumber >= 0) {
|
|
bigDecimal = bigDecimal.setScale(fractionalNumber, RoundingMode.HALF_UP);
|
|
}
|
|
result = bigDecimal.toString();
|
|
if(config.getBoolean("hideEmptyFractionalPart")) {
|
|
// Strip zero fractional: 12.00 -> 12
|
|
if(bigDecimal.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) == 0 && result.contains(".")) {
|
|
result = result.substring(0, result.indexOf('.'));
|
|
}
|
|
// Strip zeros from suffixed numbers: 1.20M -> 1.2M
|
|
if(stripTrailingZeros && result.contains(".")) {
|
|
result = result.replaceAll("0+$", "");
|
|
}
|
|
}
|
|
}
|
|
result = result.replace(".", config.getString("decimalMark"));
|
|
Message resultMessage = Message.fromString(result);
|
|
resultMessage.prepend(before);
|
|
resultMessage.append(after);
|
|
return resultMessage.getSingle();
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if the string is a correct time period.
|
|
* @param time String that has to be checked
|
|
* @return true if format is correct, false if not
|
|
*/
|
|
public static boolean checkTimeFormat(String time) {
|
|
// Check if the string is not empty and check the length
|
|
if(time == null || time.length() <= 1 || time.indexOf(' ') == -1 || time.indexOf(' ') >= (time.length() - 1)) {
|
|
return false;
|
|
}
|
|
|
|
// Check if the suffix is one of these values
|
|
String suffix = time.substring(time.indexOf(' ') + 1);
|
|
if(!identifiers.contains(suffix)) {
|
|
return false;
|
|
}
|
|
|
|
// check if the part before the space is a number
|
|
String prefix = time.substring(0, (time.indexOf(' ')));
|
|
return prefix.matches("\\d+");
|
|
}
|
|
|
|
/**
|
|
* Methode to tranlate a duration string to a millisecond value.
|
|
* @param duration The duration string
|
|
* @return The duration in milliseconds translated from the durationstring, or if it is invalid then 0
|
|
*/
|
|
public static long durationStringToLong(String duration) {
|
|
if(duration == null) {
|
|
return 0;
|
|
} else if(duration.equalsIgnoreCase("disabled") || duration.equalsIgnoreCase("unlimited") || duration.length() == 0) {
|
|
return -1;
|
|
} else if(duration.indexOf(' ') == -1) {
|
|
return 0;
|
|
}
|
|
Calendar calendar = Calendar.getInstance();
|
|
calendar.setTimeInMillis(0);
|
|
|
|
String durationString = duration.substring(duration.indexOf(' ') + 1);
|
|
int durationInt = 0;
|
|
try {
|
|
durationInt = Integer.parseInt(duration.substring(0, duration.indexOf(' ')));
|
|
} catch(NumberFormatException exception) {
|
|
// No Number found, add zero
|
|
}
|
|
|
|
if(seconds.contains(durationString)) {
|
|
calendar.add(Calendar.SECOND, durationInt);
|
|
} else if(minutes.contains(durationString)) {
|
|
calendar.add(Calendar.MINUTE, durationInt);
|
|
} else if(hours.contains(durationString)) {
|
|
calendar.add(Calendar.HOUR, durationInt);
|
|
} else if(days.contains(durationString)) {
|
|
calendar.add(Calendar.DAY_OF_MONTH, durationInt);
|
|
} else if(weeks.contains(durationString)) {
|
|
calendar.add(Calendar.DAY_OF_MONTH, durationInt * 7);
|
|
} else if(months.contains(durationString)) {
|
|
calendar.add(Calendar.MONTH, durationInt);
|
|
} else if(years.contains(durationString)) {
|
|
calendar.add(Calendar.YEAR, durationInt);
|
|
} else {
|
|
AreaShop.warn("Unknown duration indicator:", durationString, "check if config.yml has the correct time indicators");
|
|
}
|
|
return calendar.getTimeInMillis();
|
|
}
|
|
|
|
// LEGACY TIME INPUT CONVERSION
|
|
|
|
/**
|
|
* Get setting from config that could be only a number indicating seconds.
|
|
* or a string indicating a duration string
|
|
* @param path Path of the setting to read
|
|
* @return milliseconds that the setting indicates
|
|
*/
|
|
public static long getDurationFromSecondsOrString(String path) {
|
|
if(config.isLong(path) || config.isInt(path)) {
|
|
long setting = config.getLong(path);
|
|
if(setting != -1) {
|
|
setting = setting * 1000;
|
|
}
|
|
return setting;
|
|
} else {
|
|
return durationStringToLong(config.getString(path));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get setting from config that could be only a number indicating minutes.
|
|
* or a string indicating a duration string.
|
|
* @param path Path of the setting to read
|
|
* @return milliseconds that the setting indicates
|
|
*/
|
|
public static long getDurationFromMinutesOrString(String path) {
|
|
if(config.isLong(path) || config.isInt(path)) {
|
|
long setting = config.getLong(path);
|
|
if(setting != -1) {
|
|
setting = setting * 60 * 1000;
|
|
}
|
|
return setting;
|
|
} else {
|
|
return durationStringToLong(config.getString(path));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse a time setting that could be minutes or a duration string.
|
|
* @param input The string to parse
|
|
* @return milliseconds that the string indicates
|
|
*/
|
|
public static long getDurationFromMinutesOrStringInput(String input) {
|
|
long number;
|
|
try {
|
|
number = Long.parseLong(input);
|
|
if(number != -1) {
|
|
number = number * 60 * 1000;
|
|
}
|
|
return number;
|
|
} catch(NumberFormatException e) {
|
|
return durationStringToLong(input);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse a time setting that could be seconds or a duration string.
|
|
* @param input The string to parse
|
|
* @return seconds that the string indicates
|
|
*/
|
|
public static long getDurationFromSecondsOrStringInput(String input) {
|
|
long number;
|
|
try {
|
|
number = Long.parseLong(input);
|
|
if(number != -1) {
|
|
number = number * 1000;
|
|
}
|
|
return number;
|
|
} catch(NumberFormatException e) {
|
|
return durationStringToLong(input);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if an input is numeric.
|
|
* @param input The input to check
|
|
* @return true if the input is numeric, otherwise false
|
|
*/
|
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
|
public static boolean isNumeric(String input) {
|
|
try {
|
|
Integer.parseInt(input);
|
|
return true;
|
|
} catch(NumberFormatException ignored) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a string is a double.
|
|
* @param input The input
|
|
* @return true if the input is a double, otherwise false
|
|
*/
|
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
|
public static boolean isDouble(String input) {
|
|
try {
|
|
Double.parseDouble(input);
|
|
return true;
|
|
} catch(NumberFormatException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Evaluate string input to a number.
|
|
* Uses JavaScript for expressions.
|
|
* @param input The input string
|
|
* @param region The region to apply replacements for and use for logging
|
|
* @return double evaluated from the input or a very high default in case of a script exception
|
|
*/
|
|
public static double evaluateToDouble(String input, GeneralRegion region) {
|
|
// Replace variables
|
|
input = Message.fromString(input).replacements(region).getSingle();
|
|
|
|
// Check for simple number
|
|
if(isDouble(input)) {
|
|
return Double.parseDouble(input);
|
|
}
|
|
|
|
// Lazy init scriptEngine
|
|
if(scriptEngine == null) {
|
|
scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript");
|
|
}
|
|
|
|
// Evaluate expression
|
|
Object result;
|
|
try {
|
|
result = scriptEngine.eval(input);
|
|
} catch(ScriptException e) {
|
|
AreaShop.warn("Price of region", region.getName(), "is set with an invalid expression: '" + input + "', exception:", ExceptionUtils.getStackTrace(e));
|
|
return 99999999999.0; // High fallback for safety
|
|
}
|
|
|
|
// Handle the result
|
|
if(Utils.isDouble(result.toString())) {
|
|
return Double.parseDouble(result.toString());
|
|
} else {
|
|
AreaShop.warn("Price of region", region.getName(), "is set with the expression '" + input + "' that returns a result that is not a number:", result);
|
|
return 99999999999.0; // High fallback for safety
|
|
}
|
|
}
|
|
|
|
// NAME <-> UUID CONVERSION
|
|
|
|
/**
|
|
* Conversion to name by uuid.
|
|
* @param uuid The uuid in string format
|
|
* @return the name of the player
|
|
*/
|
|
public static String toName(String uuid) {
|
|
String result = "";
|
|
if(uuid != null) {
|
|
try {
|
|
UUID parsed = UUID.fromString(uuid);
|
|
result = toName(parsed);
|
|
} catch(IllegalArgumentException e) {
|
|
// Incorrect UUID
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Conversion to name by uuid object.
|
|
* @param uuid The uuid in string format
|
|
* @return the name of the player
|
|
*/
|
|
public static String toName(UUID uuid) {
|
|
if(uuid == null) {
|
|
return "";
|
|
} else {
|
|
String name = Bukkit.getOfflinePlayer(uuid).getName();
|
|
if(name != null) {
|
|
return name;
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Conversion from name to uuid.
|
|
* @param name The name of the player
|
|
* @return The uuid of the player
|
|
*/
|
|
@SuppressWarnings("deprecation") // Fake deprecation by Bukkit to inform developers, method will stay
|
|
public static String toUniqueId(String name) {
|
|
if(name == null) {
|
|
return null;
|
|
} else {
|
|
return Bukkit.getOfflinePlayer(name).getUniqueId().toString();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|