    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.contexts.CommandResultSupplier;
    027import co.aikar.commands.sponge.contexts.OnlinePlayer;
    028import org.jetbrains.annotations.Nullable;
    029import org.spongepowered.api.Sponge;
    030import org.spongepowered.api.command.CommandSource;
    031import org.spongepowered.api.entity.living.player.Player;
    032import org.spongepowered.api.entity.living.player.User;
    033import org.spongepowered.api.service.user.UserStorageService;
    034import org.spongepowered.api.text.format.TextColor;
    035import org.spongepowered.api.text.format.TextStyle;
    036import org.spongepowered.api.world.World;
    037
    038import java.util.HashSet;
    039import java.util.Optional;
    040import java.util.Set;
    041import java.util.regex.Pattern;
    042import java.util.stream.Collectors;
    043import java.util.stream.Stream;
    044
    045@SuppressWarnings("WeakerAccess")
    046public class SpongeCommandContexts extends CommandContexts<SpongeCommandExecutionContext> {
    047
    048    public SpongeCommandContexts(final SpongeCommandManager manager) {
    049        super(manager);
    050
    051        registerIssuerOnlyContext(CommandResultSupplier.class, c -> new CommandResultSupplier());
    052        registerContext(OnlinePlayer.class, c -> getOnlinePlayer(c.getIssuer(), c.popFirstArg(), c.isOptional()));
    053        registerContext(co.aikar.commands.contexts.OnlinePlayer.class, c -> {
    054            OnlinePlayer onlinePlayer = getOnlinePlayer(c.getIssuer(), c.popFirstArg(), c.isOptional());
    055            return onlinePlayer != null ? new co.aikar.commands.contexts.OnlinePlayer(onlinePlayer.getPlayer()) : null;
    056        });
    057        registerContext(User.class, c -> {
    058            String name = c.popFirstArg();
    059            // try online players first
    060            Optional<Player> targetPlayer = Sponge.getGame().getServer().getPlayer(name);
    061            if (targetPlayer.isPresent()) {
    062                return targetPlayer.get();
    063            }
    064
    065            Optional<UserStorageService> service = Sponge.getGame().getServiceManager().provide(UserStorageService.class);
    066            if (!service.isPresent()) {
    067                manager.log(LogLevel.ERROR, "No UserStorageService is available", new Error());
    068                throw new InvalidCommandArgument(MessageKeys.ERROR_GENERIC_LOGGED, false);
    069            }
    070            Optional<User> user = service.get().get(name);
    071            if (user.isPresent()) {
    072                return user.get();
    073            }
    074            if (!c.isOptional()) {
    075                throw new InvalidCommandArgument(MinecraftMessageKeys.NO_PLAYER_FOUND, false, "{search}", name);
    076            }
    077
    078            return null;
    079        });
    080        registerContext(TextColor.class, c -> {
    081            String first = c.popFirstArg();
    082            Stream<TextColor> colours = Sponge.getRegistry().getAllOf(TextColor.class).stream();
    083            String filter = c.getFlagValue("filter", (String) null);
    084            if (filter != null) {
    085                filter = ACFUtil.simplifyString(filter);
    086                String finalFilter = filter;
    087                colours = colours.filter(colour -> finalFilter.equals(ACFUtil.simplifyString(colour.getName())));
    088            }
    089            Stream<TextColor> finalColours = colours;
    090            return Sponge.getRegistry().getType(TextColor.class, ACFUtil.simplifyString(first)).orElseThrow(() -> {
    091                String valid = finalColours
    092                        .map(colour -> "<c2>" + ACFUtil.simplifyString(colour.getName()) + "</c2>")
    093                        .collect(Collectors.joining("<c1>,</c1> "));
    094                return new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", valid);
    095            });
    096        });
    097        registerContext(TextStyle.Base.class, c -> {
    098            String first = c.popFirstArg();
    099            Stream<TextStyle.Base> styles = Sponge.getRegistry().getAllOf(TextStyle.Base.class).stream();
    100            String filter = c.getFlagValue("filter", (String) null);
    101            if (filter != null) {
    102                filter = ACFUtil.simplifyString(filter);
    103                String finalFilter = filter;
    104                styles = styles.filter(style -> finalFilter.equals(ACFUtil.simplifyString(style.getName())));
    105            }
    106            Stream<TextStyle.Base> finalStyles = styles;
    107            return Sponge.getRegistry().getType(TextStyle.Base.class, ACFUtil.simplifyString(first)).orElseThrow(() -> {
    108                String valid = finalStyles
    109                        .map(style -> "<c2>" + ACFUtil.simplifyString(style.getName()) + "</c2>")
    110                        .collect(Collectors.joining("<c1>,</c1> "));
    111                return new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", valid);
    112            });
    113        });
    114
    115        registerIssuerAwareContext(CommandSource.class, SpongeCommandExecutionContext::getSource);
    116        registerIssuerAwareContext(Player.class, (c) -> {
    117            Player player = c.getSource() instanceof Player ? (Player) c.getSource() : null;
    118            if (player == null && !c.isOptional()) {
    119                throw new InvalidCommandArgument(MessageKeys.NOT_ALLOWED_ON_CONSOLE, false);
    120            }
    121            /*PlayerInventory inventory = player != null ? player.getInventory() : null;
    122            if (inventory != null && c.hasFlag("itemheld") && !ACFBukkitUtil.isValidItem(inventory.getItem(inventory.getHeldItemSlot()))) {
    123                throw new InvalidCommandArgument(MinecraftMessageKeys.YOU_MUST_BE_HOLDING_ITEM, false);
    124            }*/
    125            return player;
    126        });
    127        registerContext(OnlinePlayer[].class, (c) -> {
    128            SpongeCommandIssuer issuer = c.getIssuer();
    129            final String search = c.popFirstArg();
    130            boolean allowMissing = c.hasFlag("allowmissing");
    131            Set<OnlinePlayer> players = new HashSet<>();
    132            Pattern split = ACFPatterns.COMMA;
    133            String splitter = c.getFlagValue("splitter", (String) null);
    134            if (splitter != null) {
    135                split = Pattern.compile(Pattern.quote(splitter));
    136            }
    137            for (String lookup : split.split(search)) {
    138                OnlinePlayer player = getOnlinePlayer(issuer, lookup, allowMissing);
    139                if (player != null) {
    140                    players.add(player);
    141                }
    142            }
    143            if (players.isEmpty() && !c.hasFlag("allowempty")) {
    144                issuer.sendError(MinecraftMessageKeys.NO_PLAYER_FOUND_SERVER,
    145                        "{search}", search);
    146
    147                throw new InvalidCommandArgument(false);
    148            }
    149            return players.toArray(new OnlinePlayer[players.size()]);
    150        });
    151        registerIssuerAwareContext(World.class, (c) -> {
    152            String firstArg = c.getFirstArg();
    153            java.util.Optional<World> world = firstArg != null ? Sponge.getServer().getWorld(firstArg) : java.util.Optional.empty();
    154            if (world.isPresent()) {
    155                c.popFirstArg();
    156            }
    157            if (!world.isPresent() && c.getSource() instanceof Player) {
    158                world = java.util.Optional.of(((Player) c.getSource()).getWorld());
    159            }
    160            if (!world.isPresent()) {
    161                throw new InvalidCommandArgument(MinecraftMessageKeys.INVALID_WORLD);
    162            }
    163            return world.get();
    164        });
    165    }
    166
    167    @Nullable
    168    OnlinePlayer getOnlinePlayer(SpongeCommandIssuer issuer, String lookup, boolean allowMissing) throws InvalidCommandArgument {
    169        Player player = ACFSpongeUtil.findPlayerSmart(issuer, lookup);
    170        if (player == null) {
    171            if (allowMissing) {
    172                return null;
    173            }
    174            throw new InvalidCommandArgument(false);
    175        }
    176        return new OnlinePlayer(player);
    177    }
    178}