001/* 002 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining 005 * a copy of this software and associated documentation files (the 006 * "Software"), to deal in the Software without restriction, including 007 * without limitation the rights to use, copy, modify, merge, publish, 008 * distribute, sublicense, and/or sell copies of the Software, and to 009 * permit persons to whom the Software is furnished to do so, subject to 010 * the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be 013 * included in all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 016 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 017 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 018 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 019 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 020 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 021 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 022 */ 023 024package co.aikar.commands; 025 026import org.bukkit.Bukkit; 027import org.bukkit.ChatColor; 028import org.bukkit.Location; 029import org.bukkit.Material; 030import org.bukkit.World; 031import org.bukkit.command.CommandSender; 032import org.bukkit.entity.Entity; 033import org.bukkit.entity.Player; 034import org.bukkit.inventory.ItemStack; 035import org.jetbrains.annotations.NotNull; 036import org.jetbrains.annotations.Nullable; 037 038import java.util.ArrayList; 039import java.util.Iterator; 040import java.util.List; 041import java.util.Set; 042import java.util.regex.Pattern; 043import java.util.stream.Collectors; 044 045public class ACFBukkitUtil { 046 047 public static String formatLocation(Location loc) { 048 if (loc == null) { 049 return null; 050 } 051 return loc.getWorld().getName() + 052 ":" + 053 loc.getBlockX() + 054 "," + 055 loc.getBlockY() + 056 "," + 057 loc.getBlockZ(); 058 } 059 060 public static String color(String message) { 061 return ChatColor.translateAlternateColorCodes('&', message); 062 } 063 064 /** 065 * Move to Message Keys on the CommandIssuer 066 * 067 * @deprecated 068 */ 069 @Deprecated 070 public static void sendMsg(CommandSender player, String message) { 071 message = color(message); 072 for (String msg : ACFPatterns.NEWLINE.split(message)) { 073 player.sendMessage(msg); 074 } 075 } 076 077 public static Location stringToLocation(String storedLoc) { 078 return stringToLocation(storedLoc, null); 079 } 080 081 public static Location stringToLocation(String storedLoc, World forcedWorld) { 082 if (storedLoc == null) { 083 return null; 084 } 085 String[] args = ACFPatterns.COLON.split(storedLoc); 086 if (args.length >= 4 || (args.length == 3 && forcedWorld != null)) { 087 String world = forcedWorld != null ? forcedWorld.getName() : args[0]; 088 int i = args.length == 3 ? 0 : 1; 089 double x = Double.parseDouble(args[i]); 090 double y = Double.parseDouble(args[i + 1]); 091 double z = Double.parseDouble(args[i + 2]); 092 Location loc = new Location(Bukkit.getWorld(world), x, y, z); 093 if (args.length >= 6) { 094 loc.setPitch(Float.parseFloat(args[4])); 095 loc.setYaw(Float.parseFloat(args[5])); 096 } 097 return loc; 098 } else if (args.length == 2) { 099 String[] args2 = ACFPatterns.COMMA.split(args[1]); 100 if (args2.length == 3) { 101 String world = forcedWorld != null ? forcedWorld.getName() : args[0]; 102 double x = Double.parseDouble(args2[0]); 103 double y = Double.parseDouble(args2[1]); 104 double z = Double.parseDouble(args2[2]); 105 return new Location(Bukkit.getWorld(world), x, y, z); 106 } 107 } 108 return null; 109 } 110 111 public static String fullLocationToString(Location loc) { 112 if (loc == null) { 113 return null; 114 } 115 return (new StringBuilder(64)) 116 .append(loc.getWorld().getName()) 117 .append(':') 118 .append(ACFUtil.precision(loc.getX(), 4)) 119 .append(':') 120 .append(ACFUtil.precision(loc.getY(), 4)) 121 .append(':') 122 .append(ACFUtil.precision(loc.getZ(), 4)) 123 .append(':') 124 .append(ACFUtil.precision(loc.getPitch(), 4)) 125 .append(':') 126 .append(ACFUtil.precision(loc.getYaw(), 4)) 127 .toString(); 128 } 129 130 public static String fullBlockLocationToString(Location loc) { 131 if (loc == null) { 132 return null; 133 } 134 return (new StringBuilder(64)) 135 .append(loc.getWorld().getName()) 136 .append(':') 137 .append(loc.getBlockX()) 138 .append(':') 139 .append(loc.getBlockY()) 140 .append(':') 141 .append(loc.getBlockZ()) 142 .append(':') 143 .append(ACFUtil.precision(loc.getPitch(), 4)) 144 .append(':') 145 .append(ACFUtil.precision(loc.getYaw(), 4)) 146 .toString(); 147 } 148 149 public static String blockLocationToString(Location loc) { 150 if (loc == null) { 151 return null; 152 } 153 154 return (new StringBuilder(32)) 155 .append(loc.getWorld().getName()) 156 .append(':') 157 .append(loc.getBlockX()) 158 .append(':') 159 .append(loc.getBlockY()) 160 .append(':') 161 .append(loc.getBlockZ()) 162 .toString(); 163 } 164 165 public static double distance(@NotNull Entity e1, @NotNull Entity e2) { 166 return distance(e1.getLocation(), e2.getLocation()); 167 } 168 169 public static double distance2d(@NotNull Entity e1, @NotNull Entity e2) { 170 return distance2d(e1.getLocation(), e2.getLocation()); 171 } 172 173 public static double distance2d(@NotNull Location loc1, @NotNull Location loc2) { 174 loc1 = loc1.clone(); 175 loc1.setY(loc2.getY()); 176 return distance(loc1, loc2); 177 } 178 179 public static double distance(@NotNull Location loc1, @NotNull Location loc2) { 180 if (loc1.getWorld() != loc2.getWorld()) { 181 return 0; 182 } 183 return loc1.distance(loc2); 184 } 185 186 public static Location getTargetLoc(Player player) { 187 return getTargetLoc(player, 128); 188 } 189 190 public static Location getTargetLoc(Player player, int maxDist) { 191 return getTargetLoc(player, maxDist, 1.5); 192 } 193 194 public static Location getTargetLoc(Player player, int maxDist, double addY) { 195 try { 196 Location target = player.getTargetBlock((Set<Material>) null, maxDist).getLocation(); 197 target.setY(target.getY() + addY); 198 return target; 199 } catch (Exception ignored) { 200 return null; 201 } 202 } 203 204 public static Location getRandLoc(Location loc, int radius) { 205 return getRandLoc(loc, radius, radius, radius); 206 } 207 208 public static Location getRandLoc(Location loc, int xzRadius, int yRadius) { 209 return getRandLoc(loc, xzRadius, yRadius, xzRadius); 210 } 211 212 @NotNull 213 public static Location getRandLoc(Location loc, int xRadius, int yRadius, int zRadius) { 214 Location newLoc = loc.clone(); 215 newLoc.setX(ACFUtil.rand(loc.getX() - xRadius, loc.getX() + xRadius)); 216 newLoc.setY(ACFUtil.rand(loc.getY() - yRadius, loc.getY() + yRadius)); 217 newLoc.setZ(ACFUtil.rand(loc.getZ() - zRadius, loc.getZ() + zRadius)); 218 return newLoc; 219 } 220 221 222 public static String removeColors(String msg) { 223 return ChatColor.stripColor(color(msg)); 224 } 225 226 public static String replaceChatString(String message, String replace, String with) { 227 return replaceChatString(message, Pattern.compile(Pattern.quote(replace), Pattern.CASE_INSENSITIVE), with); 228 } 229 230 public static String replaceChatString(String message, Pattern replace, String with) { 231 final String[] split = replace.split(message + "1"); 232 233 if (split.length < 2) { 234 return replace.matcher(message).replaceAll(with); 235 } 236 message = split[0]; 237 238 for (int i = 1; i < split.length; i++) { 239 final String prev = ChatColor.getLastColors(message); 240 message += with + prev + split[i]; 241 } 242 return message.substring(0, message.length() - 1); 243 } 244 245 public static boolean isWithinDistance(@NotNull Player p1, @NotNull Player p2, int dist) { 246 return isWithinDistance(p1.getLocation(), p2.getLocation(), dist); 247 } 248 249 public static boolean isWithinDistance(@NotNull Location loc1, @NotNull Location loc2, int dist) { 250 return loc1.getWorld() == loc2.getWorld() && loc1.distance(loc2) <= dist; 251 } 252 253 /** 254 * Please move to the CommandIssuer version 255 * 256 * @deprecated 257 */ 258 public static Player findPlayerSmart(CommandSender requester, String search) { 259 CommandManager manager = CommandManager.getCurrentCommandManager(); 260 if (manager != null) { 261 return findPlayerSmart(manager.getCommandIssuer(requester), search); 262 } 263 throw new IllegalStateException("You may not use the ACFBukkitUtil#findPlayerSmart(CommandSender) async to the command execution."); 264 } 265 266 public static Player findPlayerSmart(CommandIssuer issuer, String search) { 267 CommandSender requester = issuer.getIssuer(); 268 if (search == null) { 269 return null; 270 } 271 String name = ACFUtil.replace(search, ":confirm", ""); 272 List<Player> matches = Bukkit.getServer().matchPlayer(name); 273 List<Player> confirmList = new ArrayList<>(); 274 findMatches(search, requester, matches, confirmList); 275 276 277 if (matches.size() > 1 || confirmList.size() > 1) { 278 String allMatches = matches.stream().map(Player::getName).collect(Collectors.joining(", ")); 279 issuer.sendError(MinecraftMessageKeys.MULTIPLE_PLAYERS_MATCH, 280 "{search}", name, "{all}", allMatches); 281 return null; 282 } 283 284 //noinspection Duplicates 285 if (matches.isEmpty()) { 286 if (!issuer.getManager().isValidName(name)) { 287 issuer.sendError(MinecraftMessageKeys.IS_NOT_A_VALID_NAME, "{name}", name); 288 return null; 289 } 290 Player player = ACFUtil.getFirstElement(confirmList); 291 if (player == null) { 292 issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER, "{search}", name); 293 return null; 294 } else { 295 issuer.sendInfo(MinecraftMessageKeys.PLAYER_IS_VANISHED_CONFIRM, "{vanished}", player.getName()); 296 return null; 297 } 298 } 299 300 return matches.get(0); 301 } 302 303 private static void findMatches(String search, CommandSender requester, List<Player> matches, List<Player> confirmList) { 304 // Remove vanished players from smart matching. 305 Iterator<Player> iter = matches.iterator(); 306 //noinspection Duplicates 307 while (iter.hasNext()) { 308 Player player = iter.next(); 309 if (requester instanceof Player && !((Player) requester).canSee(player)) { 310 if (requester.hasPermission("acf.seevanish")) { 311 if (!search.endsWith(":confirm")) { 312 confirmList.add(player); 313 iter.remove(); 314 } 315 } else { 316 iter.remove(); 317 } 318 } 319 } 320 } 321 322 public static boolean isValidName(@Nullable String name) { 323 return name != null && !name.isEmpty() && ACFPatterns.VALID_NAME_PATTERN.matcher(name).matches(); 324 } 325 326 static boolean isValidItem(ItemStack item) { 327 return item != null && item.getType() != Material.AIR && item.getAmount() > 0; 328 } 329}