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 co.aikar.commands.bukkit.contexts.OnlinePlayer; 027import org.bukkit.Bukkit; 028import org.bukkit.ChatColor; 029import org.bukkit.Location; 030import org.bukkit.OfflinePlayer; 031import org.bukkit.World; 032import org.bukkit.command.BlockCommandSender; 033import org.bukkit.command.CommandSender; 034import org.bukkit.entity.Entity; 035import org.bukkit.entity.Player; 036import org.bukkit.inventory.PlayerInventory; 037import org.jetbrains.annotations.Contract; 038 039import java.util.HashSet; 040import java.util.Set; 041import java.util.UUID; 042import java.util.regex.Pattern; 043import java.util.stream.Collectors; 044import java.util.stream.Stream; 045 046import static co.aikar.commands.ACFBukkitUtil.isValidName; 047 048@SuppressWarnings("WeakerAccess") 049public class BukkitCommandContexts extends CommandContexts<BukkitCommandExecutionContext> { 050 051 public BukkitCommandContexts(BukkitCommandManager manager) { 052 super(manager); 053 054 registerContext(OnlinePlayer.class, c -> getOnlinePlayer(c.getIssuer(), c.popFirstArg(), false)); 055 registerContext(co.aikar.commands.contexts.OnlinePlayer.class, c -> { 056 OnlinePlayer onlinePlayer = getOnlinePlayer(c.getIssuer(), c.popFirstArg(), false); 057 return new co.aikar.commands.contexts.OnlinePlayer(onlinePlayer.getPlayer()); 058 }); 059 registerContext(OnlinePlayer[].class, (c) -> { 060 BukkitCommandIssuer issuer = c.getIssuer(); 061 final String search = c.popFirstArg(); 062 boolean allowMissing = c.hasFlag("allowmissing"); 063 Set<OnlinePlayer> players = new HashSet<>(); 064 Pattern split = ACFPatterns.COMMA; 065 String splitter = c.getFlagValue("splitter", (String) null); 066 if (splitter != null) { 067 split = Pattern.compile(Pattern.quote(splitter)); 068 } 069 for (String lookup : split.split(search)) { 070 OnlinePlayer player = getOnlinePlayer(issuer, lookup, allowMissing); 071 if (player != null) { 072 players.add(player); 073 } 074 } 075 if (players.isEmpty() && !c.hasFlag("allowempty")) { 076 issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER, 077 "{search}", search); 078 079 throw new InvalidCommandArgument(false); 080 } 081 return players.toArray(new OnlinePlayer[players.size()]); 082 }); 083 registerIssuerAwareContext(World.class, (c) -> { 084 String firstArg = c.getFirstArg(); 085 World world = firstArg != null ? Bukkit.getWorld(firstArg) : null; 086 if (world != null) { 087 c.popFirstArg(); 088 } 089 if (world == null && c.getSender() instanceof Player) { 090 world = ((Entity) c.getSender()).getWorld(); 091 } 092 if (world == null) { 093 throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD); 094 } 095 return world; 096 }); 097 registerIssuerAwareContext(CommandSender.class, BukkitCommandExecutionContext::getSender); 098 registerIssuerAwareContext(Player.class, (c) -> { 099 boolean isOptional = c.isOptional(); 100 CommandSender sender = c.getSender(); 101 boolean isPlayerSender = sender instanceof Player; 102 if (!c.hasFlag("other")) { 103 Player player = isPlayerSender ? (Player) sender : null; 104 if (player == null && !isOptional) { 105 throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false); 106 } 107 PlayerInventory inventory = player != null ? player.getInventory() : null; 108 if (inventory != null && c.hasFlag("itemheld") && !ACFBukkitUtil.isValidItem(inventory.getItem(inventory.getHeldItemSlot()))) { 109 throw new InvalidCommandArgument(MinecraftMessageKeys.YOU_MUST_BE_HOLDING_ITEM, false); 110 } 111 return player; 112 } else { 113 String arg = c.popFirstArg(); 114 if (arg == null && isOptional) { 115 if (c.hasFlag("defaultself")) { 116 if (isPlayerSender) { 117 return (Player) sender; 118 } else { 119 throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false); 120 } 121 } else { 122 return null; 123 } 124 } else if (arg == null) { 125 throw new InvalidCommandArgument(); 126 } 127 128 OnlinePlayer onlinePlayer = getOnlinePlayer(c.getIssuer(), arg, false); 129 return onlinePlayer.getPlayer(); 130 } 131 }); 132 registerContext(OfflinePlayer.class, c -> { 133 String name = c.popFirstArg(); 134 OfflinePlayer offlinePlayer; 135 if (c.hasFlag("uuid")) { 136 UUID uuid; 137 try { 138 uuid = UUID.fromString(name); 139 } catch (IllegalArgumentException e) { 140 throw new InvalidCommandArgument(MinecraftMessageKeys.NO_PLAYER_FOUND_OFFLINE, 141 "{search}", name); 142 } 143 offlinePlayer = Bukkit.getOfflinePlayer(uuid); 144 } else { 145 offlinePlayer = Bukkit.getOfflinePlayer(name); 146 } 147 if (offlinePlayer == null || (!offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline())) { 148 if (!c.hasFlag("uuid") && !manager.isValidName(name)) { 149 throw new InvalidCommandArgument(MinecraftMessageKeys.IS_NOT_A_VALID_NAME, "{name}", name); 150 } 151 throw new InvalidCommandArgument(MinecraftMessageKeys.NO_PLAYER_FOUND_OFFLINE, 152 "{search}", name); 153 } 154 return offlinePlayer; 155 }); 156 registerContext(ChatColor.class, c -> { 157 String first = c.popFirstArg(); 158 Stream<ChatColor> colors = Stream.of(ChatColor.values()); 159 if (c.hasFlag("colorsonly")) { 160 colors = colors.filter(color -> color.ordinal() <= 0xF); 161 } 162 String filter = c.getFlagValue("filter", (String) null); 163 if (filter != null) { 164 filter = ACFUtil.simplifyString(filter); 165 String finalFilter = filter; 166 colors = colors.filter(color -> finalFilter.equals(ACFUtil.simplifyString(color.name()))); 167 } 168 169 ChatColor match = ACFUtil.simpleMatch(ChatColor.class, first); 170 if (match == null) { 171 String valid = colors 172 .map(color -> "<c2>" + ACFUtil.simplifyString(color.name()) + "</c2>") 173 .collect(Collectors.joining("<c1>,</c1> ")); 174 175 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", valid); 176 } 177 return match; 178 }); 179 registerContext(Location.class, c -> { 180 String input = c.popFirstArg(); 181 CommandSender sender = c.getSender(); 182 String[] split = ACFPatterns.COLON.split(input, 2); 183 if (split.length == 0) { 184 throw new InvalidCommandArgument(true); 185 } 186 if (split.length < 2 && !(sender instanceof Player) && !(sender instanceof BlockCommandSender)) { 187 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_WORLD); 188 } 189 final String world; 190 final String rest; 191 Location sourceLoc = null; 192 if (split.length == 2) { 193 world = split[0]; 194 rest = split[1]; 195 } else if (sender instanceof Player) { 196 sourceLoc = ((Player) sender).getLocation(); 197 world = sourceLoc.getWorld().getName(); 198 rest = split[0]; 199 } else if (sender instanceof BlockCommandSender) { 200 sourceLoc = ((BlockCommandSender) sender).getBlock().getLocation(); 201 world = sourceLoc.getWorld().getName(); 202 rest = split[0]; 203 } else { 204 throw new InvalidCommandArgument(true); 205 } 206 207 boolean rel = rest.startsWith("~"); 208 split = ACFPatterns.COMMA.split(rel ? rest.substring(1) : rest); 209 if (split.length < 3) { 210 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ); 211 } 212 213 Double x = ACFUtil.parseDouble(split[0], rel ? 0.0D : null); 214 Double y = ACFUtil.parseDouble(split[1], rel ? 0.0D : null); 215 Double z = ACFUtil.parseDouble(split[2], rel ? 0.0D : null); 216 217 if (sourceLoc != null && rel) { 218 x += sourceLoc.getX(); 219 y += sourceLoc.getY(); 220 z += sourceLoc.getZ(); 221 } else if (rel) { 222 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_CONSOLE_NOT_RELATIVE); 223 } 224 225 if (x == null || y == null || z == null) { 226 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ); 227 } 228 229 World worldObj = Bukkit.getWorld(world); 230 if (worldObj == null) { 231 throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD); 232 } 233 234 if (split.length >= 5) { 235 Float yaw = ACFUtil.parseFloat(split[3]); 236 Float pitch = ACFUtil.parseFloat(split[4]); 237 238 if (pitch == null || yaw == null) { 239 throw new InvalidCommandArgument(MinecraftMessageKeys.LOCATION_PLEASE_SPECIFY_XYZ); 240 } 241 return new Location(worldObj, x, y, z, yaw, pitch); 242 } else { 243 return new Location(worldObj, x, y, z); 244 } 245 }); 246 247 if (manager.mcMinorVersion >= 12) { 248 BukkitCommandContexts_1_12.register(this); 249 } 250 } 251 252 @Contract("_,_,false -> !null") 253 OnlinePlayer getOnlinePlayer(BukkitCommandIssuer issuer, String lookup, boolean allowMissing) throws InvalidCommandArgument { 254 Player player = ACFBukkitUtil.findPlayerSmart(issuer, lookup); 255 //noinspection Duplicates 256 if (player == null) { 257 if (allowMissing) { 258 return null; 259 } 260 throw new InvalidCommandArgument(false); 261 } 262 return new OnlinePlayer(player); 263 } 264}