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