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; 036 037import java.util.ArrayList; 038import java.util.Iterator; 039import java.util.List; 040import java.util.Set; 041import java.util.regex.Pattern; 042import java.util.stream.Collectors; 043 044public class ACFBukkitUtil { 045 046 public static String formatLocation(Location loc) { 047 if (loc == null) { 048 return null; 049 } 050 return loc.getWorld().getName() + 051 ":" + 052 loc.getBlockX() + 053 "," + 054 loc.getBlockY() + 055 "," + 056 loc.getBlockZ(); 057 } 058 059 public static String color(String message) { 060 return ChatColor.translateAlternateColorCodes('&', message); 061 } 062 063 /** 064 * Move to Message Keys on the CommandIssuer 065 * @deprecated 066 */ 067 @Deprecated 068 public static void sendMsg(CommandSender player, String message) { 069 message = color(message); 070 for (String msg : ACFPatterns.NEWLINE.split(message)) { 071 player.sendMessage(msg); 072 } 073 } 074 075 public static Location stringToLocation(String storedLoc) { 076 return stringToLocation(storedLoc, null); 077 } 078 public static Location stringToLocation(String storedLoc, World forcedWorld) { 079 if (storedLoc == null) { 080 return null; 081 } 082 String[] args = ACFPatterns.COLON.split(storedLoc); 083 if (args.length >= 4 || (args.length == 3 && forcedWorld != null)) { 084 String world = forcedWorld != null ? forcedWorld.getName() : args[0]; 085 int i = args.length == 3 ? 0 : 1; 086 double x = Double.parseDouble(args[i]); 087 double y = Double.parseDouble(args[i + 1]); 088 double z = Double.parseDouble(args[i + 2]); 089 Location loc = new Location(Bukkit.getWorld(world), x, y, z); 090 if (args.length >= 6) { 091 loc.setPitch(Float.parseFloat(args[4])); 092 loc.setYaw(Float.parseFloat(args[5])); 093 } 094 return loc; 095 } else if (args.length == 2) { 096 String[] args2 = ACFPatterns.COMMA.split(args[1]); 097 if (args2.length == 3) { 098 String world = forcedWorld != null ? forcedWorld.getName() : args[0]; 099 double x = Double.parseDouble(args2[0]); 100 double y = Double.parseDouble(args2[1]); 101 double z = Double.parseDouble(args2[2]); 102 return new Location(Bukkit.getWorld(world), x, y, z); 103 } 104 } 105 return null; 106 } 107 108 public static String fullLocationToString(Location loc) { 109 if (loc == null) { 110 return null; 111 } 112 return (new StringBuilder(64)) 113 .append(loc.getWorld().getName()) 114 .append(':') 115 .append(ACFUtil.precision(loc.getX(), 4)) 116 .append(':') 117 .append(ACFUtil.precision(loc.getY(), 4)) 118 .append(':') 119 .append(ACFUtil.precision(loc.getZ(), 4)) 120 .append(':') 121 .append(ACFUtil.precision(loc.getPitch(), 4)) 122 .append(':') 123 .append(ACFUtil.precision(loc.getYaw(), 4)) 124 .toString(); 125 } 126 127 public static String fullBlockLocationToString(Location loc) { 128 if (loc == null) { 129 return null; 130 } 131 return (new StringBuilder(64)) 132 .append(loc.getWorld().getName()) 133 .append(':') 134 .append(loc.getBlockX()) 135 .append(':') 136 .append(loc.getBlockY()) 137 .append(':') 138 .append(loc.getBlockZ()) 139 .append(':') 140 .append(ACFUtil.precision(loc.getPitch(), 4)) 141 .append(':') 142 .append(ACFUtil.precision(loc.getYaw(), 4)) 143 .toString(); 144 } 145 146 public static String blockLocationToString(Location loc) { 147 if (loc == null) { 148 return null; 149 } 150 151 return (new StringBuilder(32)) 152 .append(loc.getWorld().getName()) 153 .append(':') 154 .append(loc.getBlockX()) 155 .append(':') 156 .append(loc.getBlockY()) 157 .append(':') 158 .append(loc.getBlockZ()) 159 .toString(); 160 } 161 162 public static double distance(@NotNull Entity e1, @NotNull Entity e2) { 163 return distance(e1.getLocation(), e2.getLocation()); 164 } 165 public static double distance2d(@NotNull Entity e1, @NotNull Entity e2) { 166 return distance2d(e1.getLocation(), e2.getLocation()); 167 } 168 public static double distance2d(@NotNull Location loc1, @NotNull Location loc2) { 169 loc1 = loc1.clone(); 170 loc1.setY(loc2.getY()); 171 return distance(loc1, loc2); 172 } 173 public static double distance(@NotNull Location loc1, @NotNull Location loc2) { 174 if (loc1.getWorld() != loc2.getWorld()) { 175 return 0; 176 } 177 return loc1.distance(loc2); 178 } 179 180 public static Location getTargetLoc(Player player) { 181 return getTargetLoc(player, 128); 182 } 183 public static Location getTargetLoc(Player player, int maxDist) { 184 return getTargetLoc(player, maxDist, 1.5); 185 } 186 public static Location getTargetLoc(Player player, int maxDist, double addY) { 187 try { 188 Location target = player.getTargetBlock((Set<Material>) null, maxDist).getLocation(); 189 target.setY(target.getY() + addY); 190 return target; 191 } catch (Exception ignored) { 192 return null; 193 } 194 } 195 196 public static Location getRandLoc(Location loc, int radius) { 197 return getRandLoc(loc, radius, radius, radius); 198 } 199 public static Location getRandLoc(Location loc, int xzRadius, int yRadius) { 200 return getRandLoc(loc, xzRadius, yRadius, xzRadius); 201 } 202 @NotNull public static Location getRandLoc(Location loc, int xRadius, int yRadius, int zRadius) { 203 Location newLoc = loc.clone(); 204 newLoc.setX(ACFUtil.rand(loc.getX()-xRadius, loc.getX()+xRadius)); 205 newLoc.setY(ACFUtil.rand(loc.getY()-yRadius, loc.getY()+yRadius)); 206 newLoc.setZ(ACFUtil.rand(loc.getZ()-zRadius, loc.getZ()+zRadius)); 207 return newLoc; 208 } 209 210 211 public static String removeColors(String msg) { 212 return ChatColor.stripColor(color(msg)); 213 } 214 215 public static String replaceChatString(String message, String replace, String with) { 216 return replaceChatString(message, Pattern.compile(Pattern.quote(replace), Pattern.CASE_INSENSITIVE), with); 217 } 218 public static String replaceChatString(String message, Pattern replace, String with) { 219 final String[] split = replace.split(message + "1"); 220 221 if (split.length < 2) { 222 return replace.matcher(message).replaceAll(with); 223 } 224 message = split[0]; 225 226 for (int i = 1; i < split.length; i++) { 227 final String prev = ChatColor.getLastColors(message); 228 message += with + prev + split[i]; 229 } 230 return message.substring(0, message.length() - 1); 231 } 232 233 public static boolean isWithinDistance(@NotNull Player p1, @NotNull Player p2, int dist) { 234 return isWithinDistance(p1.getLocation(), p2.getLocation(), dist); 235 } 236 public static boolean isWithinDistance(@NotNull Location loc1, @NotNull Location loc2, int dist) { 237 return loc1.getWorld() == loc2.getWorld() && loc1.distance(loc2) <= dist; 238 } 239 240 /** 241 * Please move to the CommandIssuer version 242 * @deprecated 243 */ 244 public static Player findPlayerSmart(CommandSender requester, String search) { 245 CommandManager manager = CommandManager.getCurrentCommandManager(); 246 if (manager != null) { 247 return findPlayerSmart(manager.getCommandIssuer(requester), search); 248 } 249 throw new IllegalStateException("You may not use the ACFBukkitUtil#findPlayerSmart(CommandSender) async to the command execution."); 250 } 251 252 public static Player findPlayerSmart(CommandIssuer issuer, String search) { 253 CommandSender requester = issuer.getIssuer(); 254 if (search == null) { 255 return null; 256 } 257 String name = ACFUtil.replace(search, ":confirm", ""); 258 259 if (!isValidName(name)) { 260 issuer.sendError(MinecraftMessageKeys.IS_NOT_A_VALID_NAME, "{name}", name); 261 return null; 262 } 263 264 List<Player> matches = Bukkit.getServer().matchPlayer(name); 265 List<Player> confirmList = new ArrayList<>(); 266 findMatches(search, requester, matches, confirmList); 267 268 269 if (matches.size() > 1 || confirmList.size() > 1) { 270 String allMatches = matches.stream().map(Player::getName).collect(Collectors.joining(", ")); 271 issuer.sendError(MinecraftMessageKeys.MULTIPLE_PLAYERS_MATCH, 272 "{search}", name, "{all}", allMatches); 273 return null; 274 } 275 276 //noinspection Duplicates 277 if (matches.isEmpty()) { 278 if (confirmList.isEmpty()) { 279 issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER, 280 "{search}", name); 281 return null; 282 } else { 283 Player player = ACFUtil.getFirstElement(confirmList); 284 issuer.sendInfo(MinecraftMessageKeys.PLAYER_IS_VANISHED_CONFIRM, "{vanished}", player.getName()); 285 return null; 286 } 287 } 288 289 return matches.get(0); 290 } 291 292 private static void findMatches(String search, CommandSender requester, List<Player> matches, List<Player> confirmList) { 293 // Remove vanished players from smart matching. 294 Iterator<Player> iter = matches.iterator(); 295 //noinspection Duplicates 296 while (iter.hasNext()) { 297 Player player = iter.next(); 298 if (requester instanceof Player && !((Player) requester).canSee(player)) { 299 if (requester.hasPermission("acf.seevanish")) { 300 if (!search.endsWith(":confirm")) { 301 confirmList.add(player); 302 iter.remove(); 303 } 304 } else { 305 iter.remove(); 306 } 307 } 308 } 309 } 310 311 312 public static boolean isValidName(String name) { 313 return name != null && !name.isEmpty() && ACFPatterns.VALID_NAME_PATTERN.matcher(name).matches(); 314 } 315 316 static boolean isValidItem(ItemStack item) { 317 return item != null && item.getType() != Material.AIR && item.getAmount() > 0; 318 } 319}